mattabledatasource example angular angular-material refresh

angular - example - mattabledatasource



Angular+Material: cómo actualizar una fuente de datos(mat-table) (19)

// esta es la fuente de datos
this.guests = [];

this.guests.push ({id: 1, nombre: ''Ricardo''});

// actualiza el dataSource this.guests = Array.from (this.guest);

Estoy usando una mat-table para enumerar el contenido de los idiomas elegidos por los usuarios. También pueden agregar nuevos idiomas usando el panel de diálogo. Después agregaron un idioma y regresaron. Quiero que mi fuente de datos se actualice para mostrar los cambios que hicieron.

Inicializo el almacén de datos obteniendo datos de usuario de un servicio y pasándolos a una fuente de datos en el método de actualización.

Language.component.ts

import { Component, OnInit } from ''@angular/core''; import { LanguageModel, LANGUAGE_DATA } from ''../../../../models/language.model''; import { LanguageAddComponent } from ''./language-add/language-add.component''; import { AuthService } from ''../../../../services/auth.service''; import { LanguageDataSource } from ''./language-data-source''; import { LevelbarComponent } from ''../../../../directives/levelbar/levelbar.component''; import { DataSource } from ''@angular/cdk/collections''; import { Observable } from ''rxjs/Observable''; import ''rxjs/add/observable/of''; import { MatSnackBar, MatDialog } from ''@angular/material''; @Component({ selector: ''app-language'', templateUrl: ''./language.component.html'', styleUrls: [''./language.component.scss''] }) export class LanguageComponent implements OnInit { displayedColumns = [''name'', ''native'', ''code'', ''level'']; teachDS: any; user: any; constructor(private authService: AuthService, private dialog: MatDialog) { } ngOnInit() { this.refresh(); } add() { this.dialog.open(LanguageAddComponent, { data: { user: this.user }, }).afterClosed().subscribe(result => { this.refresh(); }); } refresh() { this.authService.getAuthenticatedUser().subscribe((res) => { this.user = res; this.teachDS = new LanguageDataSource(this.user.profile.languages.teach); }); } }

language-data-source.ts

import {MatPaginator, MatSort} from ''@angular/material''; import {DataSource} from ''@angular/cdk/collections''; import {Observable} from ''rxjs/Observable''; import ''rxjs/add/observable/merge''; import ''rxjs/add/operator/map''; export class LanguageDataSource extends DataSource<any> { constructor(private languages) { super(); } connect(): Observable<any> { return Observable.of(this.languages); } disconnect() { // No-op } }

Así que intenté llamar a un método de actualización donde obtengo al usuario del backend nuevamente y luego reinicio la fuente de datos. Sin embargo, esto no funciona, no se están produciendo cambios.


Active una detección de cambio utilizando ChangeDetectorRef en el método refresh() justo después de recibir los nuevos datos, inyecte ChangeDetectorRef en el constructor y use detectChanges esta manera:

import { Component, OnInit, ChangeDetectorRef } from ''@angular/core''; import { LanguageModel, LANGUAGE_DATA } from ''../../../../models/language.model''; import { LanguageAddComponent } from ''./language-add/language-add.component''; import { AuthService } from ''../../../../services/auth.service''; import { LanguageDataSource } from ''./language-data-source''; import { LevelbarComponent } from ''../../../../directives/levelbar/levelbar.component''; import { DataSource } from ''@angular/cdk/collections''; import { Observable } from ''rxjs/Observable''; import ''rxjs/add/observable/of''; import { MatSnackBar, MatDialog } from ''@angular/material''; @Component({ selector: ''app-language'', templateUrl: ''./language.component.html'', styleUrls: [''./language.component.scss''] }) export class LanguageComponent implements OnInit { displayedColumns = [''name'', ''native'', ''code'', ''level'']; teachDS: any; user: any; constructor(private authService: AuthService, private dialog: MatDialog, private changeDetectorRefs: ChangeDetectorRef) { } ngOnInit() { this.refresh(); } add() { this.dialog.open(LanguageAddComponent, { data: { user: this.user }, }).afterClosed().subscribe(result => { this.refresh(); }); } refresh() { this.authService.getAuthenticatedUser().subscribe((res) => { this.user = res; this.teachDS = new LanguageDataSource(this.user.profile.languages.teach); this.changeDetectorRefs.detectChanges(); }); } }


Como está usando MatPaginator , solo necesita hacer algún cambio en el paginador, esto desencadena la recarga de datos.

Truco simple:

this.paginator._changePageSize(this.paginator.pageSize);

Esto actualiza el tamaño de página al tamaño de página actual, por lo que básicamente no cambia nada, excepto que también se llama a la función privada _emitPageEvent() , lo que desencadena la recarga de la tabla.


Creo que el objeto MatTableDataSource está vinculado de alguna manera con la matriz de datos que pasa al constructor MatTableDataSource .

Por ejemplo:

dataTable: string[]; tableDS: MatTableDataSource<string>; ngOnInit(){ // here your pass dataTable to the dataSource this.tableDS = new MatTableDataSource(this.dataTable); }

Entonces, cuando tienes que cambiar datos; cambiar en la lista original dataTable y luego reflejar el cambio en la tabla mediante la llamada _updateChangeSubscription() método _updateChangeSubscription() en tableDS .

Por ejemplo:

this.dataTable.push(''testing''); this.tableDS._updateChangeSubscription();

Eso es trabajar conmigo a través de Angular 6.


Después de leer la tabla de materiales que no actualizaba la actualización de datos de publicación # 11638, el informe de errores me pareció que la mejor (leer, la solución más fácil) fue la sugerida por el comentarista final ''shhdharmen'' con una sugerencia de usar un EventEmitter.

Esto implica algunos cambios simples en la clase de fuente de datos generada

es decir) agregar una nueva variable privada a su clase de fuente de datos

import { EventEmitter } from ''@angular/core''; ... private tableDataUpdated = new EventEmitter<any>();

y donde envío nuevos datos a la matriz interna (this.data), emito un evento.

public addRow(row:myRowInterface) { this.data.push(row); this.tableDataUpdated.emit(); }

y finalmente, cambie la matriz ''dataMutation'' en el método ''connect'', de la siguiente manera

const dataMutations = [ this.tableDataUpdated, this.paginator.page, this.sort.sortChange ];


Entonces, para mí, nadie dio la buena respuesta al problema que conocí, que es casi lo mismo que @Kay. Para mí se trata de ordenar, la tabla de clasificación no produce cambios en el tapete. Me propongo esta respuesta ya que es el único tema que encuentro al buscar en Google. Estoy usando Angular 6.

Como se dice here :

Como la tabla optimiza el rendimiento, no verificará automáticamente los cambios en la matriz de datos. En cambio, cuando se agregan, eliminan o mueven objetos en la matriz de datos, puede activar una actualización de las filas renderizadas de la tabla llamando a su método renderRows ().

Entonces solo tiene que llamar a renderRows () en su método refresh () para que aparezcan los cambios.

Ver here para la integración.


Esto es trabajo para mí:

dataSource = new MatTableDataSource<Dict>([]); public search() { let url = `${Constants.API.COMMON}/dicts?page=${this.page.number}&` + (this.name == '''' ? '''' : `name_like=${this.name}`); this._http.get<Dict>(url).subscribe((data)=> { // this.dataSource = data[''_embedded''].dicts; this.dataSource.data = data[''_embedded''].dicts; this.page = data[''page'']; this.resetSelection(); }); }

Por lo tanto, debe declarar su instancia de MatTableDataSource datos como MatTableDataSource


Esto funcionó para mí:

refreshTableSorce() { this.dataSource = new MatTableDataSource<Element>(this.newSource); }


Investigué un poco más y encontré este lugar para darme lo que necesitaba: se siente limpio y se relaciona con los datos de actualización cuando se actualiza desde el servidor: https://blog.angular-university.io/angular-material-data-table/

La mayoría de los créditos a la página de arriba. A continuación se muestra una muestra de cómo se puede usar un selector de mat para actualizar una tabla de mat a un origen de datos en el cambio de selección. Estoy usando Angular 7. Perdón por ser extenso, tratando de ser completo pero conciso: he eliminado tantas partes no necesarias como sea posible. ¡Con esta esperanza de ayudar a alguien más a avanzar más rápido!

organization.model.ts:

export class Organization { id: number; name: String; }

organization.service.ts:

import { Observable, empty } from ''rxjs''; import { of } from ''rxjs''; import { Organization } from ''./organization.model''; export class OrganizationService { getConstantOrganizations(filter: String): Observable<Organization[]> { if (filter === "All") { let Organizations: Organization[] = [ { id: 1234, name: ''Some data'' } ]; return of(Organizations); } else { let Organizations: Organization[] = [ { id: 5678, name: ''Some other data'' } ]; return of(Organizations); } // ...just a sample, other filterings would go here - and of course data instead fetched from server. }

organizationdatasource.model.ts:

import { CollectionViewer, DataSource } from ''@angular/cdk/collections''; import { Observable, BehaviorSubject, of } from ''rxjs''; import { catchError, finalize } from "rxjs/operators"; import { OrganizationService } from ''./organization.service''; import { Organization } from ''./organization.model''; export class OrganizationDataSource extends DataSource<Organization> { private organizationsSubject = new BehaviorSubject<Organization[]>([]); private loadingSubject = new BehaviorSubject<boolean>(false); public loading$ = this.loadingSubject.asObservable(); constructor(private organizationService: OrganizationService, ) { super(); } loadOrganizations(filter: String) { this.loadingSubject.next(true); return this.organizationService.getOrganizations(filter).pipe( catchError(() => of([])), finalize(() => this.loadingSubject.next(false)) ).subscribe(organization => this.organizationsSubject.next(organization)); } connect(collectionViewer: CollectionViewer): Observable<Organization[]> { return this.organizationsSubject.asObservable(); } disconnect(collectionViewer: CollectionViewer): void { this.organizationsSubject.complete(); this.loadingSubject.complete(); } }

Organizations.component.html:

<div class="spinner-container" *ngIf="organizationDataSource.loading$ | async"> <mat-spinner></mat-spinner> </div> <div> <form [formGroup]="formGroup"> <mat-form-field fxAuto> <div fxLayout="row"> <mat-select formControlName="organizationSelectionControl" (selectionChange)="updateOrganizationSelection()"> <mat-option *ngFor="let organizationSelectionAlternative of organizationSelectionAlternatives" [value]="organizationSelectionAlternative"> {{organizationSelectionAlternative.name}} </mat-option> </mat-select> </div> </mat-form-field> </form> </div> <mat-table fxLayout="column" [dataSource]="organizationDataSource"> <ng-container matColumnDef="name"> <mat-header-cell *matHeaderCellDef>Name</mat-header-cell> <mat-cell *matCellDef="let organization">{{organization.name}}</mat-cell> </ng-container> <ng-container matColumnDef="number"> <mat-header-cell *matHeaderCellDef>Number</mat-header-cell> <mat-cell *matCellDef="let organization">{{organization.number}}</mat-cell> </ng-container> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row> </mat-table>

organizaciones.component.scss:

.spinner-container { height: 360px; width: 390px; position: fixed; }

organization.component.ts:

import { Component, OnInit } from ''@angular/core''; import { FormGroup, FormBuilder } from ''@angular/forms''; import { Observable } from ''rxjs''; import { OrganizationService } from ''./organization.service''; import { Organization } from ''./organization.model''; import { OrganizationDataSource } from ''./organizationdatasource.model''; @Component({ selector: ''organizations'', templateUrl: ''./organizations.component.html'', styleUrls: [''./organizations.component.scss''] }) export class OrganizationsComponent implements OnInit { public displayedColumns: string[]; public organizationDataSource: OrganizationDataSource; public formGroup: FormGroup; public organizationSelectionAlternatives = [{ id: 1, name: ''All'' }, { id: 2, name: ''With organization update requests'' }, { id: 3, name: ''With contact update requests'' }, { id: 4, name: ''With order requests'' }] constructor( private formBuilder: FormBuilder, private organizationService: OrganizationService) { } ngOnInit() { this.formGroup = this.formBuilder.group({ ''organizationSelectionControl'': [] }) const toSelect = this.organizationSelectionAlternatives.find(c => c.id == 1); this.formGroup.get(''organizationSelectionControl'').setValue(toSelect); this.organizationDataSource = new OrganizationDataSource(this.organizationService); this.displayedColumns = [''name'', ''number'' ]; this.updateOrganizationSelection(); } updateOrganizationSelection() { this.organizationDataSource.loadOrganizations(this.formGroup.get(''organizationSelectionControl'').value.name); } }


La mejor manera de hacerlo es agregando un observable adicional a su implementación de Datasource.

En el método de conexión, ya debería estar utilizando Observable.merge para suscribirse a una matriz de observables que incluyen paginator.page, sort.sortChange, etc. Puede agregar un nuevo asunto a esto y llamar a continuación cuando necesite causarlo. Una actualización.

algo como esto:

export class LanguageDataSource extends DataSource<any> { recordChange$ = new Subject(); constructor(private languages) { super(); } connect(): Observable<any> { const changes = [ this.recordChange$ ]; return Observable.merge(...changes) .switchMap(() => return Observable.of(this.languages)); } disconnect() { // No-op } }

Y luego puede llamar a recordChange$.next() para iniciar una actualización.

Naturalmente, envolvería la llamada en un método refresh () y la cancelaría de la instancia de fuente de datos con el componente, y otras técnicas adecuadas.


Logré una buena solución usando dos recursos:

refrescando dataSource y paginator:

this.dataSource.data = this.users; this.dataSource.connect().next(this.users); this.paginator._changePageSize(this.paginator.pageSize);

donde, por ejemplo, dataSource se define aquí:

users: User[]; ... dataSource = new MatTableDataSource(this.users); ... this.dataSource.paginator = this.paginator; ...


No sé si se requirió ChangeDetectorRef cuando se creó la pregunta, pero ahora esto es suficiente:

import { MatTableDataSource } from ''@angular/material/table''; // ... dataSource = new MatTableDataSource<MyDataType>(); refresh() { this.myService.doSomething().subscribe((data: MyDataType[]) => { this.dataSource.data = data; } }

Ejemplo:
StackBlitz


Puede actualizar fácilmente los datos de la tabla usando "concat":

por ejemplo:

language.component.ts

teachDS: any[] = [];

language.component.html

<table mat-table [dataSource]="teachDS" class="list">

Y, cuando actualiza los datos (language.component.ts):

addItem() { // newItem is the object added to the list using a form or other way this.teachDS = this.teachDS.concat([newItem]); }

Cuando usa "concat" angular, detecte los cambios del objeto (this.teachDS) y no necesita usar otra cosa.

PD: Es un trabajo para mí en angular 6 y 7, no probé otra versión.


Simplemente puede usar la función de conexión de fuente de datos

this.datasource.connect().next(data);

al igual que. ''datos'' son los nuevos valores para la tabla de datos


bueno, me encontré con un problema similar en el que agrego algo a la fuente de datos y no se vuelve a cargar de la manera más fácil que encontré fue simplemente volver a evaluar los datos

let dataSource = [''a'',''b'',''c''] dataSource.push(''d'') let cloned = dataSource.slice() // OR IN ES6 // let cloned = [...dataSource] dataSource = cloned


en mi caso (Angular 6+), heredé de MatTableDataSource para crear MyDataSource . Sin llamar después de this.data = someArray

this.entitiesSubject.next(this.data as T[])

datos donde no se muestran

clase MyDataSource

export class MyDataSource<T extends WhateverYouWant> extends MatTableDataSource<T> { private entitiesSubject = new BehaviorSubject<T[]>([]); loadDataSourceData(someArray: T[]){ this.data = someArray //whenever it comes from an API asyncronously or not this.entitiesSubject.next(this.data as T[])// Otherwise data not displayed } public connect(): BehaviorSubject<T[]> { return this.entitiesSubject } }//end Class


import { Subject } from ''rxjs/Subject''; import { Observable } from ''rxjs/Observable''; export class LanguageComponent implemnts OnInit { displayedColumns = [''name'', ''native'', ''code'', ''leavel'']; user: any; private update = new Subject<void>(); update$ = this.update.asObservable(); constructor(private authService: AuthService, private dialog: MatDialog) {} ngOnInit() { this.update$.subscribe(() => { this.refresh()}); } setUpdate() { this.update.next(); } add() { this.dialog.open(LanguageAddComponent, { data: { user: this.user }, }).afterClosed().subscribe(result => { this.setUpdate(); }); } refresh() { this.authService.getAuthenticatedUser().subscribe((res) => { this.user = res; this.teachDS = new LanguageDataSource(this.user.profile.languages.teach); }); } }


npm install @matheo/datasource

Lancé una biblioteca destinada a ser la fuente de datos de material oficial en el futuro, que admite cualquier tipo de flujos de entrada (clasificación, paginación, filtros) y algunas configuraciones con depuración para ver cómo funciona mientras está codificando.

import { MatDataSourceModule } from ''@matheo/datasource'';

Puede encontrar la demostración de StackBlitz y más información aquí:
https://medium.com/@matheo/reactive-datasource-for-angular-1d869b0155f6

Me gustaría escuchar su opinión y apoyar sus casos de uso si es necesario.
¡Feliz codificación!


this.dataSource = new MatTableDataSource<Element>(this.elements);

Agregue esta línea debajo de su acción de agregar o eliminar la fila en particular.

refresh() { this.authService.getAuthenticatedUser().subscribe((res) => { this.user = new MatTableDataSource<Element>(res); }); }