typescript - ¿Cómo manejar múltiples tablas creadas dinámicamente con ordenamiento en Angular?
Tengo múltiples tablas de Angular creadas dinámicamente, cada una mostrando las mismas columnas pero con datos diferentes. Necesito ordenar las columnas en cada tabla por separado. Actualmente tengo dos tablas. Cuando hago clic en la flecha del encabezado de columna en la primera, se ordena correctamente. Hacer clic en la misma columna en la segunda no hace nada.
Aquí está el HTML relevante:
<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>
Aquí está el TS relevante:
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];
};
}
¿Alguien puede ver mi error?
Respuesta #1
Necesitas asignar el orden a cada "fuenteDeDatos"
Entonces, imagino que quieres hacer algo así
this.tables.forEach((item,i)=>{
item.dataSource.sort=this.sort.find((_,index)=>index==i)
}
Mira cómo seleccionamos el QueryList usando find((_x,index)
NOTA IMPORTANTE: es
//see that is without ()
@ViewChildren(MatPaginator) paginators = new QueryList<MatPaginator>;
@ViewChildren(MatSort) sorts = new QueryList<MatSort>;
Actualización es necesario "dar tiempo a Angular" para dibujar las tablas. Podemos hacer un encapsulado en un setTimeout al asignar el matsort.
En el stackblitz utilizo algo como
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)
})
})
})
}
Actualización2 No sé sobre tu código, así que puedes crear una función
setSorts()
{
setTimeout(()=>{
this.tables.forEach((item:any,index:number)=>{
const sort=this.sorts.find((_,i:number)=>i==index)
item.dataSource.sort=sort
})
})
}
NOTA: importante su dataSource debería ser un MatTableDataSource, es decir, si tiene un array debería escribir
table.dataSource=new MatTableDataSource<Row>(yourArray);
Es necesario el setTimeout porque, cuando agregas un elemento a tu arreglo, aún no tienes orden. Necesitas que Angular "espere" a pintar las tablas. El setTimeout le dice a Angular: "¡eh! pinta las tablas y, después de haberlas pintado, ejecuta las funciones bajo el setTimeout.
Y llama siempre cada vez que agregas/quitas un elemento del arreglo "this.tables" (cada vez que haces un this.tables.push(..) o this.tables.remove)
Acerca del uso de find((_x,index)=>....), un arreglo o un queryList tiene métodos: forEach, map, find... que generalmente usamos con un único "argumento", por ejemplo `
myArray.forEach((x:any)=>{...x is the value of the element..}
Pero todos estos métodos permiten un segundo argumento que es el índice del elemento, por ejemplo.
myArray.forEach((x:any,index:number)=>{
...x is the value of the element and index the position..
console.log(x,index)
}
Cuando se usa con "find" puedes obtener el elemento en la posición "index", como no necesitamos el valor de la variable, usualmente uso un _ (pero puedes usar cualquier nombre para la variable)
