Фильтровать вложенный столбец в таблице PrimeNG (Angular)

Итак, у меня есть вложенный столбец, например. клиент.хобби.любимый. Я хочу иметь возможность фильтровать по имени 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

Есть идеи, как решить эту проблему?

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
0
50
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Прежде всего, не используйте 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>

Демо @ StackBlitz

Вы можете добавить новое свойство в объект клиента. Customer.favouriteХобби

и установите значение customer.hobbies.favourite.map((h: Any) => h.name).join(', ');

так это будет выглядеть примерно так

customer.favouriteHobbies = customer.hobbies.favourite.map((h: any) => h.name).join(', ');

Теперь обычный фильтр заливки будет работать и давать желаемый результат.

Другие вопросы по теме