Reputation: 1760
In my angular project, I have installed PrimeNG controls version 11.4.4. I have used Table control to create tabular data to show the rows in the group with collapsible style.
Now I have added a textbox and dropdown control right before the header row to filter the table data. But the problem is with Row Group table data, filtering with dropdown is not working always. Only the dropdown item Accessories is working. But for other dropdown item filtering is not working.
Can anyone please run the code to see the problem and suggest to me how to solve this?
<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 file:
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 };
}
}
}
}
}
Upvotes: 0
Views: 3837
Reputation: 51195
Checked that this logic in rowGroupMetadata
crash when the table is filtered.
<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>
You need to make sure that rowGroupMetadata
is also updated when the table is filtered.
For HTML part, rather than directly call filter
callback, you should call dropdownFilter
custom function by parsing filter
callback and $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>
dropDownFilter
method accepts filter
callback and value
parameters. In this method, after the table is filtered, would pass
the filteredValue
(filtered result) to updateRowGroupMetaData
method.updateRowGroupMetaData
method is modified that receives rows
as optional parameter. When received rows
, will update rowGroupMetadata
based on it. Otherwise, this.products
is used to remain the existing logic..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);
}
Upvotes: 1