I'm having a nested mat table in which I'm able to implement search filter for nested table. When I'm clearing the input, the table data is not being reset. Example when i check Jason row nested table data, it contains two rows. When i search for Ohio, the behaviour is expected as per my requirement. When clearing the search box, and checking the person jason row nested rows, two rows are not being displayed. It should be set to default
used
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="needed global Filter here">
toggleRow(element: User) { if (element.addresses && Array.isArray(element.addresses)) { const addresses = element.addresses as Address[]; const filteredAddresses = addresses.filter(address => { return address.street.toLowerCase().includes(this.searchFilter.toLowerCase()) || address.zipCode.toLowerCase().includes(this.searchFilter.toLowerCase()) || address.city.toLowerCase().includes(this.searchFilter.toLowerCase()); }); element.addresses = new MatTableDataSource(filteredAddresses); } this.expandedElement = this.expandedElement === element ? null : element; this.cd.detectChanges(); this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).sort = this.innerSort.toArray()[index]); } applyFilter(filterValue: string) { filterValue = filterValue.trim().toLowerCase(); if (!filterValue) { this.dataSource.filter = ''; this.expandedElement = null; return; } this.dataSource.filterPredicate = (data: User, filter: string) => { if (!data.addresses) { return data.name.toLowerCase().includes(filter) || data.email.toLowerCase().includes(filter) || data.phone.toLowerCase().includes(filter); } else { const addresses = (data.addresses as MatTableDataSource<Address>).data; const matchingAddress = addresses.find(a => a.street.toLowerCase() === filter || a.zipCode.toLowerCase() === filter || a.city.toLowerCase() === filter); if (matchingAddress) { data.addresses = new MatTableDataSource([matchingAddress]); return true; } else { return false; } } }; this.dataSource.filter = filterValue; this.expandedElement = this.dataSource.filteredData[0]; }The above code is working, but on clearing the input, the table is not resetting to original state
Here is the example stackblitz
Expected output: when I search any name in the table, it should expand the particular nested table and show that row
01 Answer
I edited your applyFilter method and added filterUsers:
applyFilter(filterValue: string) { if (filterValue === '') { this.resetFilter(); } const filterString = filterValue.trim().toLowerCase(); const filterResult = this.filterUsers(this.usersData, filterString); if (filterResult.length > 0) { this.dataSource = new MatTableDataSource( this.filterUsers(this.usersData, filterString) ); if (filterResult.length === 1) { filterResult.forEach((user: User) => this.toggleRow(user)); } } else { this.resetFilter(); } } resetFilter() { this.dataSource = new MatTableDataSource(this.usersData); this.dataSource.sort = this.sort; this.expandedElement = null; } filterUsers(users: User[], searchTerm: string) { function searchInObject(obj, term: string, visited = new Set()) { if (visited.has(obj)) { return false; } visited.add(obj); for (const key in obj) { if (typeof obj[key] === 'object') { if (searchInObject(obj[key], term, visited)) { return true; } } else if ( String(obj[key]).toLowerCase().includes(term.toLowerCase()) ) { return true; } } return false; } return users.filter((user) => searchInObject(user, searchTerm)); }I changed the keyup filter logic to use a FormControl instead since that allows you to use debounceTime:
this.filterSubscription = this.filterInput.valueChanges .pipe(debounceTime(300)) .subscribe((val) => { this.applyFilter(val); }); 1