Выпадающий фильтр не работает с таблицей групп строк в элементах управления PrimeNG в Angular

#angular #primeng #primeng-datatable

Вопрос:

В моем проекте angular я установил элементы управления PrimeNG версии 11.4.4. Я использовал элемент управления таблицей для создания табличных данных, отображающих строки в группе со складным стилем. Теперь я добавил текстовое поле и раскрывающийся элемент управления прямо перед строкой заголовка для фильтрации данных таблицы. Но проблема в том, что с данными таблицы групп строк фильтрация с помощью выпадающего списка работает не всегда. Работает только выпадающий список «Аксессуары». Но для других выпадающих элементов фильтрация не работает. Вот мое репозиторий на GitHub. Может ли кто-нибудь запустить код, чтобы увидеть проблему и предложить мне, как ее решить?

 <h2>Table with Rog Group</h2>

<p-table #dt2 [columns]="selectedColumns" [value]="products" sortField="category" sortMode="single" (onSort)="onSort()"
  dataKey="category" styleClass="p-datatable-gridlines p-datatable-striped">
  <ng-template pTemplate="header" let-columns>
    <tr>
      <th *ngFor="let col of columns">
        {{col.header}}
      </th>
    </tr>
    <tr>
      <th *ngFor="let col of columns" [ngSwitch]="col.field">
        <p-columnFilter *ngSwitchCase="'code'" type="text" field="code" matchMode="contains"
          (input)="applyFilter1($event, 'code', 'contains')">
        </p-columnFilter>

        <p-columnFilter *ngSwitchCase="'name'" type="text" field="name" matchMode="contains"
          (input)="applyFilter1($event, 'name', 'contains')">
        </p-columnFilter>

        <p-columnFilter *ngSwitchCase="'category'" field="category" matchMode="equals" [showMenu]="false">
          <ng-template pTemplate="filter" let-value let-filter="filterCallback">
            <p-dropdown [ngModel]="value" [options]="categories" (onChange)="filter($event.value)" placeholder="Any"
              [showClear]="true">
              <ng-template let-option pTemplate="item">
                <div class="p-multiselect-representative-option">
                  <span class="p-ml-1">{{option.label}}</span>
                </div>
              </ng-template>
            </p-dropdown>
          </ng-template>
        </p-columnFilter>

        <p-columnFilter *ngSwitchCase="'quantity'" type="text" field="quantity" matchMode="equals"
          (input)="dt2.filter($event.target.value1)">
        </p-columnFilter>
      </th>
    </tr>
  </ng-template>
  <ng-template pTemplate="body" let-rowData let-rowIndex="rowIndex" let-expanded="expanded">
    <tr *ngIf="rowGroupMetadata[rowData.category].index === rowIndex">
      <td colspan="4">
        <button type="button" pButton pRipple [pRowToggler]="rowData"
          class="p-button-text p-button-rounded p-button-plain p-mr-2"
          [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
        <span class="p-text-bold p-ml-2">{{rowData.category}}</span>
      </td>
    </tr>
  </ng-template>

  <ng-template pTemplate="rowexpansion" let-rowData let-columns="columns">
    <tr>
      <td *ngFor="let col of columns">
        <span>{{rowData[col.field]}}</span>
      </td>
    </tr>
  </ng-template>
</p-table>
 

Файл TS:

 import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { Table } from 'primeng/table';
import { Product } from '../_models/product.model';
import { ProductService } from '../_services/product.service';

@Component({
  selector: 'app-row-group-grid',
  templateUrl: './row-group-grid.component.html',
  styleUrls: ['./row-group-grid.component.css']
})
export class RowGroupGridComponent implements OnInit {
  products: Product[] = [];

  cols: any[] = [];
  _selectedColumns: any[] = [];
  categories: any[] = [];
  rowGroupMetadata: any;

  @ViewChild('dt2') dt2!: Table;

  constructor(private productService: ProductService) { }

  ngOnInit() {
    this.productService.getProductsSmall().then(data => {
      this.products = data;
      this.updateRowGroupMetaData();
    });

    this.cols = [
      { field: 'code', header: 'Code' },
      { field: 'name', header: 'Name' },
      { field: 'category', header: 'Category' },
      { field: 'quantity', header: 'Quantity' }
    ];

    this._selectedColumns = this.cols;

    this.categories = [      
      { label: "Clothing", value: "Clothing" },
      { label: "Electronics", value: "Electronics" },
      { label: "Fitness", value: "Fitness" },
      { label: "Accessories", value: "Accessories" },
    ];
  }

  @Input() get selectedColumns(): any[] {
    return this._selectedColumns;
  }

  set selectedColumns(val: any[]) {
    //restore original order
    this._selectedColumns = this.cols.filter(col => val.includes(col));
  }

  applyFilter1($event: any, field: string, matchMode: string) {
    let value = ($event.target as HTMLInputElement)?.value;
    this.dt2.filter(value, field, matchMode);
  }

  onSort() {
    this.updateRowGroupMetaData();
  }

  updateRowGroupMetaData() {
    this.rowGroupMetadata = {};

    if (this.products) {
      for (let i = 0; i < this.products.length; i  ) {
        let rowData = this.products[i];
        let category1 = rowData.category;

        if (i == 0) {
          this.rowGroupMetadata[category1] = { index: 0, size: 1 };
        }
        else {
          let previousRowData = this.products[i - 1];
          let previousRowGroup = previousRowData.category;

          if (category1 === previousRowGroup)
            this.rowGroupMetadata[category1].size  ;
          else
            this.rowGroupMetadata[category1] = { index: i, size: 1 };
        }
      }
    }
  }
}
 

Ответ №1:

Вопрос

Проверил, что эта логика приводит к rowGroupMetadata сбою при фильтрации таблицы.

 <tr *ngIf="rowGroupMetadata[rowData.category].index === rowIndex">
  <td colspan="4">
    <button type="button" pButton pRipple [pRowToggler]="rowData"
      class="p-button-text p-button-rounded p-button-plain p-mr-2"
      [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
    <span class="p-text-bold p-ml-2">{{rowData.category}}</span>
  </td>
</tr>
 

Вам нужно убедиться, что rowGroupMetadata это также обновляется при фильтрации таблицы.


Решение

Для HTML-части, вместо прямого filter обратного вызова, вы должны вызвать dropdownFilter пользовательскую функцию filter , проанализировав обратный вызов и $event.value .

.component.html

 <p-columnFilter *ngSwitchCase="'category'" field="category" matchMode="equals" [showMenu]="false">
  <ng-template pTemplate="filter" let-value let-filter="filterCallback">
    <p-dropdown [options]="categories" (onChange)="dropdownFilter(filter, $event.value)" placeholder="Any" [showClear]="true">
      <ng-template let-option pTemplate="item">
        <div class="p-multiselect-representative-option">
          <span class="p-ml-1">{{option.label}}</span>
        </div>
      </ng-template>
    </p-dropdown>
  </ng-template>
</p-columnFilter>
 
  1. dropDownFilter метод принимает filter обратный вызов и value
    параметры. В этом методе после фильтрации таблицы будет передан
    filteredValue (отфильтрованный результат) в updateRowGroupMetaData
    метод.
  2. updateRowGroupMetaData изменяется метод, который получает rows в качестве необязательного параметра. Когда будет получено rows , обновит rowGroupMetadata на его основе. В противном случае this.products используется существующая логика.

.компонент.ts

 updateRowGroupMetaData(rows?: Product[]) {
  let products = rows ?? this.products;
  this.rowGroupMetadata = {};

  if (products) {
    for (let i = 0; i < products.length; i  ) {
      let rowData = products[i];
      let category1 = rowData.category;
      if (i == 0) {
        this.rowGroupMetadata[category1] = { index: 0, size: 1 };
      }
      else {
        let previousRowData = products[i - 1];
        let previousRowGroup = previousRowData.category;

        if (category1 === previousRowGroup)
          this.rowGroupMetadata[category1].size  ;
        else
          this.rowGroupMetadata[category1] = { index: i, size: 1 };
      }
    }
  }
}

dropdownFilter(filter: (a: string) => void, value: string) {
  filter(value);
  this.updateRowGroupMetaData(this.dt2.filteredValue);
}
 

Образец решения на СтекбЛитце

Комментарии:

1. Я получаю эту ошибку: параметр » a «неявно имеет тип «любой». ошибка в функции dropdownFilter. Дайте мне знать, как решить эту проблему, пожалуйста.

2. Обновил свой ответ. Укажите a в качестве типа строки. dropdownFilter(filter: (a: string) => void, value: string)

3. Спасибо за решение. Сейчас это работает.