Итак, у меня есть вложенный столбец, например. клиент.хобби.любимый. Я хочу иметь возможность фильтровать по имени favourite.name.
Образец данных:
{
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',
},
],
},
},
Я извлекаю хобби, чтобы они отображались
getHobbies(customer: any): string {
return customer.hobbies.favourite.map((h: any) => h.name).join(', ');
}
Хобби HTML
<div class = "flex align-items-center">
Hobbies
<p-columnFilter
type = "text"
field = "hobbies.favourite"
display = "menu"
/>
</div>
Ряд
<td>{{ getHobbies(customer) }}</td>
Например:
Если для столбца «Хобби» я фильтрую так, чтобы он начинался со слова «футбол», тогда в нем должен быть указан Джеймс Батт.
Если фильтр содержит букву «c», то будут показаны только все строки с крикетом. В данном случае Жозефина Даракджи
Stackblitz повторяет мою проблему: https://stackblitz.com/edit/8xxfhn-hapzpy
Есть идеи, как решить эту проблему?
Прежде всего, не используйте FN в HTML, потому что это вызывает каждое обнаружение изменений " {{ getHobbies(customer) }} ", используйте вместо этого канал «{{ customer.hobbies.favourite | concatHobbies }}», это сработает только в том случае, если значение перед ним изменится.
Используйте PrimeNg FilterService, чтобы создать свой собственный фильтр, который может фильтровать вложенные объекты.
С помощью FilterService вы можете настроить фильтр (логику) для одного столбца.
<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>
Зарегистрируйте FilterService
в компоненте и добавьте ограничение фильтра: «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;
});
}
...
}
Однако глобальный фильтр не будет работать, поскольку он фильтрует данные в режиме соответствия «содержит». Пользовательский фильтр для hobbies.favourite
не применяется к глобальному фильтру.
Самый простой способ — изменить данные таблицы и интерфейс, чтобы они возвращали hobbies.favourite
как string[]
вместо вложенного массива объектов. Примените новое поле: favouriteHobbies
к интерфейсу, данным и таблице PrimeNG.
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>
Вы можете добавить новое свойство в объект клиента. Customer.favouriteХобби
и установите значение customer.hobbies.favourite.map((h: Any) => h.name).join(', ');
так это будет выглядеть примерно так
customer.favouriteHobbies = customer.hobbies.favourite.map((h: any) => h.name).join(', ');
Теперь обычный фильтр заливки будет работать и давать желаемый результат.