Angular – El orden de los elementos de la QueryList es diferente del modelo después de los cambios.
Estoy mostrando datos en una tabla utilizando el componente MatTable
de la librería Angular Material
.
En cada fila de la tabla, utilizo una directiva – appTableRow
– para escuchar eventos de clic y teclado.
En el componente padre, utilizo una QueryList
para obtener todas las directivas de la vista y, porque los datos se actualizan, también uso una suscripción para obtener los cambios en esta lista:
@ViewChildren(TableRowDirective)
public rows: QueryList<TableRowDirective> = new QueryList<TableRowDirective>();
public ngAfterViewInit(): void {
this.rows.changes
.pipe(startWith(this.rows))
.subscribe((newItems: QueryList<TableRowDirective>) => {
console.log('rows changes: ', newItems.toArray());
});
}
El problema:
Cuando el modelo de datos se actualiza (se quitan o vuelven a agregar elementos del array), la QueryList se actualiza, pero el orden de los elementos en la QueryList es diferente al orden de los elementos en el modelo de datos. Los elementos agregados están al final de la lista Query, incluso si en el modelo de datos están al principio.
* Ejemplo:*
Modelo de datos inicial: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Paso 1: eliminar los primeros 4 elementos - modelo de datos actualizado - -> [5, 6, 7, 8, 9, 10]
Paso 2: volver a agregar el primer elemento - modelo de datos actualizado - ->[1, 5, 6, 7, 8, 9, 10]
Después del paso 2, los elementos de QueryList son:
Elemento 5, Elemento 6, Elemento 7, Elemento 8, Elemento 9, Elemento 10, Elemento 1
Stackblitz:
(lo interesante es que, al usar una lista simple con un *ngFor
, los elementos de la QueryList están siempre sincronizados con el modelo de datos).
davy.ai
Solución al problema con la actualización de QueryList para el componente MatTable de Angular Material
El problema con el componente
MatTable
de Angular Material es que cuando se eliminan y vuelven a agregar elementos en el modelo de datos, el orden de los elementos en laQueryList
es diferente al del modelo de datos. Esto puede producir resultados no deseados y afecta la funcionalidad de la aplicación.Para abordar este problema, se puede implementar la siguiente solución en el componente padre:
Primero, se crea un objeto BehaviorSubject para almacenar la fuente de datos de la tabla. La variable
displayedColumns
se utiliza para definir las columnas que se deben mostrar en la tabla.El decorador
ViewChildren
se utiliza para obtener todas las directivasappTableRow
de la vista. Se observa el objetoQueryList
de los cambios utilizando el operadorchanges
, que luego llama al métodogetRows
de la tabla para actualizar la propiedadrow
de cada directiva.Dado que los cambios en
QueryList
pueden no actualizarse durante el ciclo dengOnInit
, se maneja una suscripción en el método de ciclo de vidangAfterViewInit
. El métododetectChanges
de esta solución se utiliza para actualizar la tabla cuando se realizan cambios en el modelo de datos.El método
removeItem
se utiliza para eliminar un elemento del modelo de datos. Encuentra el índice del elemento a eliminar y luego lo elimina del array. Luego establece la fuente de datos con una tabla vacía y luego la restablece con la matriz de datos actualizada.Esta solución asegura que el orden de los elementos en el objeto
QueryList
se mantenga sincronizado con el modelo de datos, independientemente de las actualizaciones que se realicen en él.Stackblitz:
https://stackblitz.com/edit/angular-ig6y8v-3ckq3a?file=src%2Fapp%2Ftable%2Ftable.component.ts