下拉过滤器无法与 PrimeNG 控件中的行组 table 一起使用

Dropdown filter is not working in with Row Group table in PrimeNG controls

在我的 angular 项目中,我安装了 PrimeNG 控件版本 11.4.4。我已使用 Table 控件创建表格数据,以显示组中的行并具有可折叠样式。

现在我在 header 行之前添加了一个文本框和下拉控件来过滤 table 数据。但问题在于行组 table 数据,使用下拉列表进行过滤并不总是有效。只有下拉项附件在工作。但是对于其他下拉项过滤不起作用。

Here is my GitHub Repo.

任何人都可以运行查看问题的代码并向我建议如何解决这个问题吗?

<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 };
        }
      }
    }
  }
}

问题

检查 rowGroupMetadata 中的这个逻辑在过滤 table 时崩溃。

<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 在过滤 table 时也会更新


解决方案

对于HTML部分,不是直接调用filter回调,而是通过解析filter回调和$event.value来调用dropdownFilter自定义函数。

.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 参数。在这个方法中,table被过滤后,会通过 filteredValue(过滤后的结果)到 updateRowGroupMetaData 方法。
  2. updateRowGroupMetaData 方法被修改为接收 rows 作为可选参数。当收到 rows 时,将根据它更新 rowGroupMetadata。否则,this.products用于保留现有逻辑。

.component.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);
}

Sample Solution on StackBlitz