J'utilise un mat-table pour lister le contenu des langues choisies par les utilisateurs. Ils peuvent également ajouter de nouvelles langues à l'aide du panneau de dialogue. Après ils ont ajouté une langue et sont revenus. Je souhaite que ma source de données soit actualisée pour afficher les modifications apportées.
J'initialise le magasin de données en récupérant les données utilisateur d'un service et en les passant à une source de données dans la méthode d'actualisation.
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
}
}
J'ai donc essayé d'appeler une méthode d'actualisation dans laquelle je récupère à nouveau l'utilisateur du serveur, puis je réinitialise la source de données. Cependant, cela ne fonctionne pas, aucun changement ne se produit.
Déclenchez une détection de changement en utilisant ChangeDetectorRef
dans la méthode refresh()
juste après avoir reçu les nouvelles données, injectez ChangeDetectorRef dans le constructeur et utilisez detectChanges comme ceci:
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();
});
}
}
Je ne sais pas si la ChangeDetectorRef
était requise lorsque la question a été créée, mais cela suffit maintenant:
import { MatTableDataSource } from '@angular/material/table';
// ...
dataSource = new MatTableDataSource<MyDataType>();
refresh() {
this.myService.doSomething().subscribe(data: MyDataType[] => {
this.dataSource.data = data;
}
}
Donc pour moi, personne n’a donné la bonne réponse au problème que j’ai rencontré, qui est presque identique à @Kay. Pour moi, il s’agit de trier, le tri de table ne se produit pas de changements dans le tapis ..__Je vise cette réponse puisque c’est le seul sujet que j’ai trouvé en cherchant sur Google .
Comme dit ici :
Dans la mesure où la table optimise les performances, elle ne recherchera pas automatiquement les modifications apportées au tableau de données. Au lieu de cela, lorsque des objets sont ajoutés, supprimés ou déplacés sur le tableau de données, vous pouvez déclencher une mise à jour des lignes rendues de la table en appelant sa méthode renderRows ().
Il vous suffit donc d'appeler renderRows () dans votre méthode refresh () pour que vos modifications apparaissent.
Voir ici pour l'intégration.
Comme vous utilisez MatPaginator
, il vous suffit de modifier la pagination, cela déclenche le rechargement des données.
Astuce simple:
this.paginator._changePageSize(this.paginator.pageSize);
Ceci met à jour la taille de la page à la taille de la page actuelle, donc en principe rien ne change, à l'exception de la fonction _emitPageEvent()
privée qui est également appelée, ce qui déclenche le rechargement de la table.
La meilleure façon de le faire est d’ajouter un élément observable supplémentaire à l’implémentation de votre source de données.
Dans la méthode de connexion, vous devriez déjà utiliser Observable.merge
pour vous abonner à un tableau d'observables comprenant le paginator.page, sort.sortChange, etc. Vous pouvez ajouter un nouveau sujet à ce sujet et appeler le suivant lorsque vous devez provoquer une actualisation. .
quelque chose comme ça:
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
}
}
Et vous pouvez ensuite appeler recordChange$.next()
pour lancer une actualisation.
Naturellement, je placerais l'appel dans une méthode refresh () et l'appellerais à partir de l'instance de source de données avec le composant, ainsi que d'autres techniques appropriées.
this.dataSource = new MatTableDataSource (this.elements);
Ajoutez cette ligne en dessous de votre action d’ajout ou de suppression de la ligne en question.
refresh() {
this.authService.getAuthenticatedUser().subscribe((res) => {
this.user = new MatTableDataSource<Element>(res);
});
}
Vous pouvez simplement utiliser la fonction de connexion à la source de données
this.datasource.connect().next(data);
ainsi. 'data' étant les nouvelles valeurs du datatable
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);
});
}
}
dans mon cas (angulaire 6+), j'ai hérité de MatTableDataSource
pour créer MyDataSource
. Sans appeler after this.data = someArray
this.entitiesSubject.next(this.data as T[])
données où non affiché
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
Vous pouvez facilement mettre à jour les données de la table en utilisant "concat":
par exemple:
language.component.ts
teachDS: any[] = [];
language.component.html
<table mat-table [dataSource]="teachDS" class="list">
Et lorsque vous mettez à jour les données (language.component.ts):
addItem() {
// newItem is the object added to the list using a form or other way
this.teachDS = this.teachDS.concat([newItem]);
}
Lorsque vous utilisez "concat" angular, détectez les modifications de l'objet (this.teachDS) et vous n'avez pas besoin d'utiliser autre chose.
PD: C'est un travail pour moi dans les angles 6 et 7, je n'ai pas essayé une autre version.
Je pense que l’objet MatTableDataSource
est en quelque sorte lié au tableau de données que vous transmettez au constructeur MatTableDataSource
.
Par exemple:
dataTable: string[];
tableDS: MatTableDataSource<string>;
ngOnInit(){
// here your pass dataTable to the dataSource
this.tableDS = new MatTableDataSource(this.dataTable);
}
Donc, quand vous devez changer les données; changer sur la liste d'origine dataTable
et ensuite refléter le changement sur la table en appelant la méthode _updateChangeSubscription()
sur tableDS
.
Par exemple:
this.dataTable.Push('testing');
this.tableDS._updateChangeSubscription();
C'est un travail avec moi à travers Angular 6.
Après avoir lu Table des matières non mise à jour après la mise à jour des données # 11638 Rapport de bogue J'ai trouvé le meilleur (lire, la solution la plus simple) suggéré par le dernier commentateur 'shhdharmen' avec une suggestion d'utiliser un EventEmitter.
Cela implique quelques modifications simples à la classe de source de données générée
c'est-à-dire) ajouter une nouvelle variable privée à votre classe de source de données
import { EventEmitter } from '@angular/core';
...
private tableDataUpdated = new EventEmitter<any>();
et où je pousse de nouvelles données vers le tableau interne (this.data), j’émets un événement.
public addRow(row:myRowInterface) {
this.data.Push(row);
this.tableDataUpdated.emit();
}
et enfin, changez le tableau 'dataMutation' dans la méthode 'connect' - comme suit
const dataMutations = [
this.tableDataUpdated,
this.paginator.page,
this.sort.sortChange
];
// c'est la source de données
this.guests = [];
this.guests.Push ({id: 1, nom: 'Ricardo'});
// actualise le dataSource this.guests = Array.from (this.guest);
J'ai fait quelques recherches supplémentaires et trouvé cet endroit pour me donner ce dont j'avais besoin - me sens propre et concerne les données de mise à jour lorsque le serveur est actualisé: https://blog.angular-university.io/angular-material-data-table/
La plupart des crédits de la page ci-dessus. Vous trouverez ci-dessous un exemple d'utilisation d'un sélecteur mat pour mettre à jour une table mat liée à une source de données lors du changement de sélection. J'utilise Angular 7. Désolé d'avoir été long, en essayant d'être complet mais concis - j'ai extrait autant de pièces inutiles que possible. En espérant aider quelqu'un d'autre à avancer plus vite!
organisation.modèle.ts:
export class Organization {
id: number;
name: String;
}
organisation.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();
}
}
organisations.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>
organisations.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);
}
}
Cela fonctionne pour moi:
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();
});
}
Vous devez donc déclarer votre instance de source de données sous la forme MatTableDataSource
Cela a fonctionné pour moi:
refreshTableSorce() {
this.dataSource = new MatTableDataSource<Element>(this.newSource);
}
J'ai réalisé une bonne solution en utilisant deux ressources:
actualiser à la fois dataSource et paginator:
this.dataSource.data = this.users;
this.dataSource.connect().next(this.users);
this.paginator._changePageSize(this.paginator.pageSize);
où par exemple dataSource est défini ici:
users: User[];
...
dataSource = new MatTableDataSource(this.users);
...
this.dataSource.paginator = this.paginator;
...
eh bien, j’ai rencontré un problème similaire dans lequel j’ajoutais quelque chose à la source de données et qui ne rechargeait pas de la manière la plus simple que j’ai trouvée, c’était simplement de rassembler les données.
let dataSource = ['a','b','c']
dataSource.Push('d')
let cloned = dataSource.slice()
// OR IN ES6 // let cloned = [...dataSource]
dataSource = cloned
npm install @matheo/datasource
J'ai publié une bibliothèque destinée à devenir la source de données matérielle officielle à l'avenir, prenant en charge tout type de flux d'entrée (tri, pagination, filtres) et une configuration avec débogage pour voir son fonctionnement pendant le codage.
import { MatDataSourceModule } from '@matheo/datasource';
Vous pouvez trouver la démo de StackBlitz et des informations complémentaires ici:
https://medium.com/@matheo/reactive-datasource-for-angular-1d869b0155f6
Je serais heureux d'entendre votre opinion et de soutenir vos cas d'utilisation si nécessaire.
Joyeux codage!