228

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. I want my datasource to refresh to show the changes they made.

I initialize the datastore by getting user data from a service and passing that into a datasource in the refresh method.

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
  }

}

So I have tried to call a refresh method where I get the user from the backend again and then I reinitialize the data source. However this does not work, no changes are occurring.

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
Kay
  • 17,906
  • 63
  • 162
  • 270
  • 1
    If you want to trigger the change "from the data source", please have a look at https://stackoverflow.com/questions/47897694/angular-material-2-how-to-refresh-md-table-after-add-data-to-datasource – Yennefer Jan 12 '19 at 14:46
  • **Event emmiters can be use in this case.** https://stackoverflow.com/a/44858648/8300620 – Rohit Parte Sep 17 '19 at 07:07

29 Answers29

156

I don't know if the ChangeDetectorRef was required when the question was created, but now this is enough:

import { MatTableDataSource } from '@angular/material/table';

// ...

dataSource = new MatTableDataSource<MyDataType>();

refresh() {
  this.myService.doSomething().subscribe((data: MyDataType[]) => {
    this.dataSource.data = data;
  }
}

Example:
StackBlitz

Martin Schneider
  • 14,263
  • 7
  • 55
  • 58
  • 8
    While this solution does in fact work, it does mess up the material paginator if the element is added somewhere other than the first page of results. I understand that that is beyond the scope of this question but since the two are related, do you happen to have a quick solution to that problem that you could add to your answer? – Knight Jun 04 '18 at 20:07
  • 7
    @Knight I think you have to assign the Paginator to the `MatTableDataSource.paginator` property after the Paginator's view has been initialized. See the description of `paginator` property here: https://material.angular.io/components/table/api#MatTableDataSource – Martin Schneider Jun 11 '18 at 15:23
  • Good reference. I hadn't spotted that in the docs before. Thanks! – Knight Jun 12 '18 at 15:05
  • 2
    @MA-Maddin can you be more specific on how to do it? example? – Nathanphan Nov 20 '18 at 12:03
83

Trigger a change detection by using ChangeDetectorRef in the refresh() method just after receiving the new data, inject ChangeDetectorRef in the constructor and use detectChanges like this:

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();
    });
  }
}
HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
  • 21
    That seems to work, is this the proper way to do it? Seems a bit hacky... – Kay Oct 14 '17 at 16:50
  • 1
    What are the other ways? Could you provide examples in your solution for a complete answer? – Kay Oct 14 '17 at 16:53
  • [Triggering Angular2 change detection manually](https://stackoverflow.com/questions/34827334/triggering-angular2-change-detection-manually) – HDJEMAI Oct 14 '17 at 17:23
  • this made my who matTable become unresponsive. Must have done something wrong. – Jeff Stapleton Mar 31 '21 at 06:18
69

So for me, nobody gave the good answer to the problem that i met which is almost the same than @Kay. For me it's about sorting, sorting table does not occur changes in the mat. I purpose this answer since it's the only topic that i find by searching google. I'm using Angular 6.

As said here:

Since the table optimizes for performance, it will not automatically check for changes to the data array. Instead, when objects are added, removed, or moved on the data array, you can trigger an update to the table's rendered rows by calling its renderRows() method.

So you just have to call renderRows() in your refresh() method to make your changes appears.

See here for integration.

4x10m
  • 842
  • 8
  • 11
42

In Angular 9, the secret is this.dataSource.data = this.dataSource.data;

Example:

import { MatTableDataSource } from '@angular/material/table';

dataSource: MatTableDataSource<MyObject>;

refresh(): void {
    this.applySomeModif();
    // Do what you want with dataSource

    this.dataSource.data = this.dataSource.data;
}

applySomeModif(): void {
    // add some data
    this.dataSource.data.push(new MyObject());
    // delete index number 4
    this.dataSource.data.splice(4, 0);
}
Nicolas
  • 2,684
  • 1
  • 16
  • 22
  • 13
    THAT IS JUST SUPPER WEIRD, shame on Angular material! – Arash Dec 03 '20 at 14:45
  • 3
    @Arash I agree with you :/ but I prefer this solution instead of the proper one with hundreds lines... – Nicolas Dec 03 '20 at 16:58
  • Underrated answer. Thanks @Nicolas, I had been scratching my head for the past 24 hours as one table was rendering properly, while another one wasn't. – Nitin Kamath Jan 20 '22 at 12:51
  • Not even this was working for me. The only thing that worked was `this.dataSource.data = this.dataSource.data.filter(a => a);` – cdm Aug 18 '23 at 08:50
34

Since you are using MatPaginator, you just need to do any change to paginator, this triggers data reload.

Simple trick:

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

This updates the page size to the current page size, so basically nothing changes, except the private _emitPageEvent() function is called too, triggeing table reload.

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
takeshin
  • 49,108
  • 32
  • 120
  • 164
  • I tried your code and it does not work (has no effect). However nextPage and then previousPage again works, but is no solution. – Ahmed Hasn. Dec 07 '17 at 13:25
  • 1
    `_changePageSize()` isn't public right? Is it safe to use? More on https://stackoverflow.com/questions/59093781/is-it-safe-to-use-angular-api-methods-with-underscore-prefix/59096028#59096028 – Jones Nov 29 '19 at 04:29
  • `this.table.renderRows();` didn't work, but your answer worked perfectly. Feels a bit hacky but does what it needs to do! – Daniël Camps Aug 02 '23 at 07:21
14

After adding a new data row, I refreshed my material table by updating dataSource without using its instance.

Table in HTML:

<table mat-table #table [dataSource]="myDataArray">

addUser() in file component.ts:

public USER_DATA: user[] = [];

public newUser = {userName: "ABC", email: "abc@gmail.com"};
public myDataArray: any;


addUser() {
    const newUsersArray = this.USER_DATA;
    newUsersArray.push(this.newUser);
    this.myDataArray = [...newUsersArray]; // Refresh the dataSource
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Isurie
  • 310
  • 4
  • 9
12
this.dataSource = new MatTableDataSource<Element>(this.elements);

Add this line below your action of add or delete the particular row.

refresh() {
  this.authService.getAuthenticatedUser().subscribe((res) => {
    this.user = new MatTableDataSource<Element>(res);   
  });
}
HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
Pooja Paigude
  • 121
  • 1
  • 4
12

You can just use the datasource connect function

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

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

cozmik05
  • 477
  • 5
  • 13
12

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

The easiest way I found was simply to reassign the data:

let dataSource = ['a','b','c']
dataSource.push('d')
let cloned = dataSource.slice()
// Or in ECMAScript 2015 (ES6): // let cloned = [...dataSource]
dataSource = cloned
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mordy Stern
  • 797
  • 6
  • 12
11

You can also use the renderRows() method:

@ViewChild(MatTable, {static: false}) table : MatTable<any>  // Initialize

then

this.table.renderRows();

For reference, check this out: Angular 10|9|8 Edit/ Add/ Delete Rows in Material Table with using Dialogs inline Row Operation

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RJ26
  • 165
  • 1
  • 8
  • thanks a lot.. was looking exactly for that, for my case i change the datasource manually and i just want to re-render the rows.. so voila! – ufk Sep 17 '22 at 07:59
9

Best way to do this is by adding an additional observable to your Datasource implementation.

In the connect method you should already be using Observable.merge to subscribe to an array of observables that include the paginator.page, sort.sortChange, etc. You can add a new subject to this and call next on it when you need to cause a refresh.

something like this:

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
    }
}

And then you can call recordChange$.next() to initiate a refresh.

Naturally I would wrap the call in a refresh() method and call it off of the datasource instance w/in the component, and other proper techniques.

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
john-g
  • 874
  • 6
  • 14
  • This method may the right way. It's works fine for me – Manu May 16 '18 at 09:05
  • how do i implement this when i want to extend MatTableDataSource? When i try your code example i get the error `Property 'connect' in type 'customDataSource' is not assignable to the same property in base type 'MatTableDataSource'. Type '() => Observable' is not assignable to type '() => BehaviorSubject'. Type 'Observable' is not assignable to type 'BehaviorSubject'. Property '_value' is missing in type 'Observable'.` – Maurice Jul 12 '18 at 17:49
  • 1
    @Maurice the type MatTableDataSource implements the connect method with a different return type. It uses BehaviorSubject which means you just need to alter example to return this instead of an Observable. You should still be able to use DataSource but if you have to use MatTableDataSource then return a BehaviorSubject that is subscribed to your observable, assuming you have one to start from. Hope that helps. You can refer to the source for MatTableDataSource for exact syntax of new datasource type: https://github.com/angular/material2/blob/master/src/lib/table/table-data-source.ts – john-g Jul 17 '18 at 14:58
  • The link to the data table component source is broken. This link should work: https://github.com/angular/components/blob/main/src/material/table/table-data-source.ts – john-g Jul 14 '22 at 22:34
6

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 of performance issues. But it looks more like a by-design issue, and they can not change it. It should be expected for the table to update when a new row occurs. If this behavior should not be enabled by default, there should be a switch to switch it off.

Anyway, 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tom Smykowski
  • 25,487
  • 54
  • 159
  • 236
6

I have tried some of the previous suggestions. It does update the table, but I have some concerns:

  1. Updating dataSource.data with its clone. e.g.

    this.dataSource.data = [...this.dataSource.data];
    

    If the data is large, this will reallocate lot of memory. Moreover, MatTable thinks that everything is new inside the table, so it may cause performance issue. I found my table flickers where my table has about 300 rows.

  2. Calling paginator._changePageSize. e.g.

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

    It will emit page event. If you have already had some handling for the page event. You may find it weird because the event may be fired more than once. And there can be a risk that if somehow the event will trigger _changePageSize() indirectly, it will cause infinite loop...

    I suggest another solution here. If your table is not relying on dataSource's filter field.

  3. You may update the filter field to trigger table refresh:

    this.dataSource.filter = ' '; // Note that it is a space, not empty string
    

    By doing so, the table will perform filtering and thus updating the UI of the table. But it requires having your own dataSource.filterPredicate() to handling your filtering logic.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Avin Shum
  • 446
  • 6
  • 5
5

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;
    ...
danilo
  • 7,680
  • 7
  • 43
  • 46
4

You can easily update the data of the table using "concat":

for example:

language.component.ts

teachDS: any[] = [];

language.component.html

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

And, when you update the data (language.component.ts):

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

When you're using "concat" angular detect the changes of the object (this.teachDS) and you don't need to use another thing.

PD: It's work for me in angular 6 and 7, I didn't try another version.

  • 2
    Yes, It works for me, is a problem about the reference and value var, the change detection doesn't see the new changes, for that you need update it. – Mayra Rodriguez Apr 09 '19 at 15:17
  • This might work if the dataSource is just an array but not when dataSource is a MatTableDataSource object. – Rui Marques Jul 15 '19 at 09:22
3

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

dataSource = [];
this.dataSource = [];
setTimeout(() => {
  this.dataSource = this.tableData[data];
}, 200)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vishesh Mishra
  • 988
  • 9
  • 12
3

In Angular 10, this is what works for me:

In the HTML:

<mat-table [dataSource]="myArray">

In the TypeScript component:

myArray: MyObject[] = [];

addObjectToTable(object:MyObject): void {
    // To prevent duplicated objects

    if (object&& !this.myArray.includes(object)) {
        this.myArray.push(object);

        // To force the data table's datasource to refresh
        this.myArray= [...this.myArray];
    }
 }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kingabdr
  • 588
  • 5
  • 12
  • I'm not sure if this is the best solution but this worked for me. If there any other better solutions please suggest. – LordDraagon Apr 26 '21 at 06:03
2

This worked for me:

refreshTableSorce() {
    this.dataSource = new MatTableDataSource<Element>(this.newSource);
}
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
  • Not an ideal solution as it recreates source for the table.And using this with streamlined/ socket is not an efficient. – Maihan Nijat Nov 21 '18 at 17:50
1
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);   
    });
  }
}
HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
  • 9
    Please add an explanation to your answer, just posting code is not very helpful and can result in your answer being removed. – Daxtron2 Apr 23 '18 at 15:41
1

in my case (Angular 6+), I inherited from MatTableDataSource to create MyDataSource. Without calling after this.data = someArray

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

data where not displayed

class 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 
Pipo
  • 4,653
  • 38
  • 47
0

I think the MatTableDataSource object is some way linked with the data array that you pass to MatTableDataSource constructor.

For instance:

dataTable: string[];
tableDS: MatTableDataSource<string>;

ngOnInit(){
   // here your pass dataTable to the dataSource
   this.tableDS = new MatTableDataSource(this.dataTable); 
}

So, when you have to change data; change on the original list dataTable and then reflect the change on the table by call _updateChangeSubscription() method on tableDS.

For instance:

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

That's work with me through Angular 6.

Nimer Awad
  • 3,967
  • 3
  • 17
  • 31
0

This is working for me:

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

So you should declare your datasource instance as MatTableDataSource

Ali
  • 2,702
  • 3
  • 32
  • 54
Tiny King
  • 121
  • 1
  • 4
0

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);
  }
}
Henrik
  • 1,897
  • 1
  • 16
  • 9
0

After reading Material Table not updating post data update #11638 Bug Report I found the best (read, the easiest solution) was as suggested by the final commentor 'shhdharmen' with a suggestion to use an EventEmitter.

This involves a few simple changes to the generated datasource class

ie) add a new private variable to your datasource class

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

and where I push new data to the internal array (this.data), I emit an event.

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

and finally, change the 'dataMutation' array in the 'connect' method - as follows

const dataMutations = [
    this.tableDataUpdated,
    this.paginator.page,
    this.sort.sortChange
];
Gav_at_HRSTS
  • 179
  • 2
  • 11
0
// this is the dataSource    
this.guests = [];

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

// refresh the dataSource
this.guests = Array.from(this.guest);
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Igor Faoro
  • 11
  • 4
0

Try this. It may help you.

Start with a function which loads users.

loadUser() {
    this.userService.getListOfUsers().subscribe()
        (response: any) => {
            this.dataSource = response
            this.dataSource.paginator = this.paginator;
        }

}

The define a refresh function, which will be used to refresh the table after deleting the user.

refresh() {
    this.loadUser();
    this.dataSource.data = [...this.dataSource.data];
    this.dataSource.paginator = this.paginator;
}

Now you can call the refresh() function after you finish deleting the user process, like below.

deleteUser() {
    ......
    this.refresh()
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
exe
  • 19
  • 1
  • 6
0

When you set up your datasource.ts file instead of using a simple array for data, use a subject and getter/setter for data like so:

 dataStream = new BehaviorSubject<DataSourceItem[]>(EXAMPLE_DATA);
 set data(v: DataSourceItem[]) { this.dataStream.next(v); }
 get data(): DataSourceItem[] { return this.dataStream.value; }

Then in your connect() function use the dataStream subject like so:

return merge(this.dataStream, this.paginator.page, this.sort.sortChange)

So now when you change your dataSource.data array...like so:

this.dataSource.data = ['my','new','item']

it will trigger the this.dataStream.next(v) in the setter and the connect() function will update your table properly.

A full working example can be found here: https://stackblitz.com/edit/angular-material2-issue-yzhsml?file=app%2Fdata-table-datasource.ts

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nik
  • 7,086
  • 6
  • 36
  • 52
0

It appears that your refresh() function is appropriately adding a new instance to the teachDS data source. The table, however, is not being updated since it is not being instructed to do so.

You may instruct Angular to monitor changes to the data source and update the table as necessary by using the ChangeDetectorRef service.

The ChangeDetectorRef service must first be injected into your component:

import { ChangeDetectorRef } from '@angular/core';

constructor(
            private authService: AuthService, 
            private dialog: MatDialog, 
            private changeDetectorRef: ChangeDetectorRef
) { }

Then, after refreshing the data source in the refresh() function, you can instruct Angular to check for changes and update the table by using the detectChanges() method on the changeDetectorRef:

refresh() {
  this.authService.getAuthenticatedUser().subscribe((res) => {
    this.user = res;
    this.teachDS = new LanguageDataSource(this.user.profile.languages.teach);
    this.changeDetectorRef.detectChanges();  // <-- Tell Angular to check for changes
  });
}

The table should be updated as a result to reflect the data source changes.

Tharindu Lakshan
  • 3,995
  • 6
  • 24
  • 44
0

In Angular 15, none of the previous solutions worked for me.

Instead, I had to use

 @ViewChild(MatTable) table!: MatTable<MyStruct>;
 ...
 this.table.renderRows();

See the StackBlitz example for an example.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dror
  • 2,370
  • 1
  • 18
  • 13