Angular + Material - How to refresh a data source (mat-table)

前端 未结 23 1213
自闭症患者
自闭症患者 2020-11-28 01:59

I am using a mat-table to list the content of the users chosen languages. They can also add new languages using dialog panel. After they added a language and returned back.

相关标签:
23条回答
  • 2020-11-28 02:47

    Well, I ran into a similar problem where I added somthing to the data source and it's not reloading.

    The easiest way I found whas simply to reassigning the data

    let dataSource = ['a','b','c']
    dataSource.push('d')
    let cloned = dataSource.slice()
    // OR IN ES6 // let cloned = [...dataSource]
    dataSource = cloned
    
    0 讨论(0)
  • 2020-11-28 02:47

    There are two ways to do it because Angular Material is inconsistent, and this is very poorly documented. Angular material table won't update when a new row will arrive. Surprisingly it is told it is because performance issues. But it looks more like a by design issue, they can not change. It should be expected for the table to update when new row occurs. If this behavior should not be enabled by default there should be a switch to switch it off.

    Anyways, we can not change Angular Material. But we can basically use a very poorly documented method to do it:

    One - if you use an array directly as a source:

    call table.renderRows()
    

    where table is ViewChild of the mat-table

    Second - if you use sorting and other features

    table.renderRows() surprisingly won't work. Because mat-table is inconsistent here. You need to use a hack to tell the source changed. You do it with this method:

    this.dataSource.data = yourDataSource;
    

    where dataSource is MatTableDataSource wrapper used for sorting and other features.

    0 讨论(0)
  • 2020-11-28 02:51

    I did some more research and found this place to give me what I needed - feels clean and relates to update data when refreshed from server: https://blog.angular-university.io/angular-material-data-table/

    Most credits to the page above. Below is a sample of how a mat-selector can be used to update a mat-table bound to a datasource on change of selection. I am using Angular 7. Sorry for being extensive, trying to be complete but concise - I have ripped out as many non-needed parts as possible. With this hoping to help someone else getting forward faster!

    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>
    

    organizations.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 讨论(0)
  • 2020-11-28 02:55

    You can just use the datasource connect function

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

    like so. 'data' being the new values for the datatable

    0 讨论(0)
  • 2020-11-28 02:56

    I achieved a good solution using two resources:

    refreshing both dataSource and paginator:

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

    where for example dataSource is defined here:

        users: User[];
        ...
        dataSource = new MatTableDataSource(this.users);
        ...
        this.dataSource.paginator = this.paginator;
        ...
    
    0 讨论(0)
  • 2020-11-28 02:56

    I had tried ChangeDetectorRef, Subject and BehaviourSubject but what works for me

    dataSource = [];
    this.dataSource = [];
     setTimeout(() =>{
         this.dataSource = this.tableData[data];
     },200)
    
    0 讨论(0)
提交回复
热议问题