Reputation: 1465
I have multiple dynamically created Angular tables, each displaying the same columns but with different data. I need to sort the columns in each table separately. I currently have two tables. When I click on the column header arrow on the first one, it sorts correctly. Clicking on the same column in the second table does nothing.
Here is the relevant html:
<div appMaterialElevation *ngFor="let item of tables; let i = index">
<table
mat-table
[dataSource]="item.dataSource"
multiTemplateDataRows
matSort
matSortActive="configName"
matSortDirection="asc"
>
<ng-container matColumnDef="configName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Table Name</th>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
Here is the relevant TS:
import { Component, ViewChild, ViewChildren, QueryList, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
export class Row {
configName: string;
}
export class FormatData {
formatName: string;
dataSource: MatTableDataSource<Row>;
selection: SelectionModel<Row>;
}
export class ConfigureFormatsComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChildren(MatPaginator) paginators = new QueryList<MatPaginator>();
@ViewChildren(MatSort) sorts = new QueryList<MatSort>();
tables: FormatData[];
displayedColumns: string[] = ['configName'];
getconfigformats() {
this.tables = [] as FormatData[];
this.myService.getMyData()
.subscribe((configList: MyConfigs[]) => {
let table = new FormatData();
let configNamesList = [] as Row[];
configList.forEach(config => {
let row = new Row();
row.configName = config.configName;
configNamesList.push(row);
});
table.dataSource = new MatTableDataSource<Row>(configNamesList);
table.selection = new SelectionModel<Row>(true, []);
this.tables.push(table);
this.ngAfterViewInit();
}
});
}
ngAfterViewInit() {
for (let i=0; i<this.tables.length; i++) {
let table = this.tables[i];
table.dataSource.sort = this.sorts.toArray()[i];
table.dataSource.paginator = this.paginators.toArray()[i];
};
}
Can anybody see my mistake?
Upvotes: 3
Views: 1484
Reputation: 57929
You need assing the sort to each "dataSource"
So,I imagine you want to make some like
this.tables.forEach((item,i)=>{
item.dataSource.sort=this.sort.find((_,index)=>index==i)
}
See how we select the QueryList using find((_x,index)
IMPORTANT NOTE: is
//see that is without ()
@ViewChildren(MatPaginator) paginators = new QueryList<MatPaginator>;
@ViewChildren(MatSort) sorts = new QueryList<MatSort>;
Update it's necesary "give time to Angular" to draw the tables. we can make enclosing into a setTimeout when assign the matsort.
In the stackblitz I use some like
dataSources:MatTableDataSource<any>[]=[]
@ViewChildren(MatSort) sorts:QueryList<MatSort>
ngOnInit()
{
service.getData().subscribe((res:any[])=>{
res.forEach((x,index)=>{
this.dataSources[index]=new MatTableDataSource(x)
})
setTimeout(()=>{
this.dataSources.forEach((x,index)=>{
x.sort=this.sorts.find((_,i)=>i==index)
})
})
})
}
Update2 I don't know about your code, so you can create a function
setSorts()
{
setTimeout(()=>{
this.tables.forEach((item:any,index:number)=>{
const sort=this.sorts.find((_,i:number)=>i==index)
item.dataSource.sort=sort
})
})
}
NOTE: important your dataSource should be a MatTableDataSource, that's. If you has an array you should write
table.dataSource=new MatTableDataSource<Row>(yourArray);
It's necesary the setTimeout because, when you add an element to your array still you have no sort. You need "wait" Angular paint the tables. the setTimeout say to Angular: "eh! paint the tables and, after you've painted execute the functions under the setTimeout.
And call always you add/remove one element to the array "this.tables" (each time you make a this.tables.push(..) or this.tables.remove)
About the use of find((_x,index)=>....), an array or a queryList has methods: forEach,map,find.. that generally we use with an unique "argument", e.g. `
myArray.forEach((x:any)=>{...x is the value of the element..}
But all this methods allow a second argument that is the index of the element, e.g.
myArray.forEach((x:any,index:number)=>{
...x is the value of the element and index the position..
console.log(x,index)
}
When you use with "find" you can get the element in the position "index", as we needn't the value of the variable, I usually use a _
(but you can use any name of the variable
Upvotes: 3