So I have a column which is nested e.g. customer.hobbies.favourite. I want to be able to filter based on the favourite.name.
Data Sample:
{
id: 1000,
name: 'James Butt',
country: {
name: 'Algeria',
code: 'dz',
},
company: 'Benton, John B Jr',
date: '2015-09-13',
status: 'unqualified',
verified: true,
activity: 17,
representative: {
name: 'Ioni Bowcher',
image: 'ionibowcher.png',
},
balance: 70663,
hobbies: {
favourite: [
{
id: 1,
name: 'football',
},
{
id: 2,
name: 'basketball',
},
],
},
},
My retrieval of hobbies to have them display
getHobbies(customer: any): string {
return customer.hobbies.favourite.map((h: any) => h.name).join(', ');
}
Hobbies HTML
<div class="flex align-items-center">
Hobbies
<p-columnFilter
type="text"
field="hobbies.favourite"
display="menu"
/>
</div>
Row
<td>{{ getHobbies(customer) }}</td>
For example:
If for the Hobbies Column, I filter so that it starts with "football" then it should show James Butt.
If I filter contains "c" then only all rows with cricket would show. In this case Josephine Darakjy
Stackblitz replicating my issue: https://stackblitz.com/edit/8xxfhn-hapzpy
Any ideas on how to solve this issue?
With FilterService, you can customize the filter (logic) for the single column.
<div class="flex align-items-center">
Hobbies
<p-columnFilter
type="text"
field="hobbies.favourite"
display="menu"
[showMatchModes]="false"
[showOperator]="false"
[showAddButton]="false"
[matchModeOptions]="{ label: 'Hobbies Contain', value: 'hobbies-contain' }"
[matchMode]="'hobbies-contain'"
/>
</div>
Register the FilterService
into the component and add the filter constraint: "hobbies-contain"
import { FilterService } from 'primeng/api';
@Component({
selector: 'table-filter-advanced-demo',
templateUrl: 'table-filter-advanced-demo.html',
standalone: true,
imports: [ImportsModule],
providers: [CustomerService, FilterService],
})
export class TableFilterAdvancedDemo implements OnInit {
...
constructor(
private customerService: CustomerService,
private filterService: FilterService
) {}
ngOnInit() {
...
this.filterService.register('hobbies-contain', (value, filter): boolean => {
if (filter === undefined || filter === null || filter.trim() === '') {
return true;
}
if (value === undefined || value === null) {
return false;
}
return this.filterFavoriteHobbies(value, filter).length > 0;
});
}
...
}
However, the global filter won't work as it filters the data with "contains" match mode. The custom filter for hobbies.favourite
does not apply to the global filter.
The easiest way is to modify your table data and interface to return the hobbies.favourite
as string[]
instead of the nested array of objects. Apply the new field: favouriteHobbies
to the interface, data and PrimeNG table.
export type CustomerWithFavoriteHobbies = Customer & {
favouriteHobbies: string[] | null;
}
customers!: CustomerWithFavoriteHobbies[];
ngOnInit() {
this.customerService.getCustomersLarge().then((customers) => {
this.customers = customers.map(x => ({
...x,
favouriteHobbies: x.hobbies?.favourite?.map(y => y.name)
}));
this.loading = false;
this.customers.forEach(
(customer) => (customer.date = new Date(<Date>customer.date))
);
});
}
<p-table
#dt1
[value]="customers"
dataKey="id"
[rows]="10"
[rowsPerPageOptions]="[10, 25, 50]"
[loading]="loading"
[paginator]="true"
[globalFilterFields]="['name', 'country.name', 'representative.name', 'status', 'favouriteHobbies']"
>
<ng-template pTemplate="header">
<tr>
...
<th style="min-width: 10rem">
<div class="flex align-items-center">
Hobbies
<p-columnFilter
type="text"
field="favouriteHobbies"
display="menu"
matchMode="contains"
/>
</div>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
...
<td>
{{ customer.favouriteHobbies?.join(', ') }}
</td>
</tr>
</ng-template>
...
</p-table>