web-dev-qa-db-fra.com

Angular + Material - Comment actualiser une source de données (mat-table)

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.

38
Kay

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();
    });
  }
}
30
HDJEMAI

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;
  }
}
24
MA-Maddin

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.

10
4x10m

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.

10
takeshin

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.

6
jogi

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);   
  });
}
5
Pooja Paigude

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

2
cozmik05
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);   
    });
  }
}
2
Machhindra Neupane

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é

classe 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 
1
Pipo

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.

0
nimeresam

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
];
0
Gav_at_ASTS

// 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);

0
Igor Faoro

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);
  }
}
0
Henrik

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

0
Tiny King

Cela a fonctionné pour moi:

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

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;
    ...
0
danilo

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
0
Mordy Stern
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!

0
Mateo Tibaquira