es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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).

Tags:  ,

Answer

  1. Avatar for 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 la QueryList 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:

    import { AfterViewChecked, AfterViewInit, Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
    import { BehaviorSubject, Observable, Subscription } from 'rxjs';
    import { startWith } from 'rxjs/operators';
    import { MatSort, MatTable, MatTableDataSource } from '@angular/material';
    import { TableRowDirective } from './table-row.directive';
    
    @Component({
      selector: 'app-table',
      templateUrl: './table.component.html',
      styleUrls: ['./table.component.scss']
    })
    export class TableComponent implements OnInit, AfterViewInit, AfterViewChecked, OnDestroy {
      dataSource$: BehaviorSubject<><any>> = new BehaviorSubject(new MatTableDataSource([]));
    
      displayedColumns: string[] = ['id', 'name', 'actions'];
      dataArray: any[] = [
        { id: 1, name: 'item 1' },
        { id: 2, name: 'item 2' },
        { id: 3, name: 'item 3' },
        { id: 4, name: 'item 4' },
        { id: 5, name: 'item 5' },
        { id: 6, name: 'item 6' },
        { id: 7, name: 'item 7' },
        { id: 8, name: 'item 8' },
        { id: 9, name: 'item 9' },
        { id: 10, name: 'item 10' }
      ];
    
      @ViewChild(MatTable, { static: false }) table: MatTable<any>;
      @ViewChildren(TableRowDirective) rows: QueryList<tablerowdirective>;
    
      private unsubscriber$: Subscription = new Subscription();
    
      constructor() {}
    
      ngOnInit(): void {
        this.dataSource$.next(new MatTableDataSource(this.dataArray));
      }
    
      ngOnDestroy(): void {
        this.unsubscriber$.unsubscribe();
      }
    
      ngAfterViewInit(): void {
        this.rows.changes.subscribe(() => {
          if (!this.table) { 
            return; 
          }
          const rows = this.table.getRows();
          this.rows.forEach((directive, index) => directive.row = rows[index]);
        });
        this.detectChanges();
      }
    
      ngAfterViewChecked(): void {
        this.detectChanges();
      }
    
      private detectChanges(): void {
        setTimeout(() => {
          if (this.dataSource$.value) {
            this.dataSource$.getValue().sort = this.sort;
            this.table.dataSource = this.dataSource$.getValue();
            this.table.renderRows();
          }
        });
      }
    
      removeItem(item): void {
        const index = this.dataArray.findIndex(i => i.id === item.id);
        if (index !== -1) {
          this.dataArray.splice(index, 1);
          this.dataSource$.next(new MatTableDataSource([]));
          this.dataSource$.next(new MatTableDataSource(this.dataArray));
        }
      }
    
    }
    

    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 directivas appTableRow de la vista. Se observa el objeto QueryList de los cambios utilizando el operador changes, que luego llama al método getRows de la tabla para actualizar la propiedad row de cada directiva.

    Dado que los cambios en QueryList pueden no actualizarse durante el ciclo de ngOnInit, se maneja una suscripción en el método de ciclo de vida ngAfterViewInit. El método detectChanges 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

Comments are closed.