52

I'm trying to get my Angular Table to refresh after updating the data used in the table.

The docs say "you can trigger an update to the table's rendered rows by calling its renderRows() method." but it is not like a normal child component where I can use something "@ViewChild(MatSort) sort: MatSort;" since I do not import it.

If I do import it and try something like @ViewChild('myTable') myTable: MatTableModule; then I get an error that says that renderRows() does not exist on that type.

How can I call this method? Thanks!

My table code snippet:

<mat-table #table [dataSource]="dataSource" myTable class="dataTable">
av0000
  • 1,917
  • 6
  • 31
  • 51

4 Answers4

112

Make sure you import ViewChild and MatTable:

import {Component, ViewChild} from '@angular/core';
import {MatTable} from '@angular/material';

Then you can get a reference to the table using the ViewChild (note that a type T is required on MatTable - I just used any, but if you have a typed table, you will need to use that type:

@ViewChild(MatTable) table: MatTable<any>;

Then when you modify the table in any way you will need to call the renderRows() method.

delete(row: any): void {
  /* delete logic here */
  this.table.renderRows();
}

Here is a very simple working example: https://stackblitz.com/edit/angular-bxrahf

Some sources I found when solving this issue myself:

Brad Lawrence
  • 1,483
  • 1
  • 11
  • 11
  • 2
    If your stackbliz preview doesn't work like mine didn't in chrome you may have 3rd party cookies blocked. An issue has been filed here: https://github.com/stackblitz/core/issues/162 as well as a workaround posted by cvietor. -> chrome://settings/content/cookies -> allow -> Add: [*.]stackblitz.io – Brad Lawrence May 23 '18 at 18:42
  • Worked like charm.!! – Adi Oct 09 '19 at 10:15
  • 7
    Since Angular 8 ViewChild requires a new "static" attribute. @ViewChild(MatTable, { static: false }) table: MatTable; more info here: https://angular.io/guide/static-query-migration – sosNiLa Nov 15 '19 at 09:17
  • Try this if still not working: `@ViewChild('table') table: MatTable;` – Abhijeet Khangarot Mar 27 '20 at 09:41
  • 2
    "In Angular version 9 and later, it will be safe to remove any {static: false} flags and we will do this cleanup for you in a schematic." Stated [here](https://angular.io/guide/static-query-migration#why-do-i-have-to-specify-static-false-isnt-that-the-default). – Filip Savic Apr 09 '20 at 21:11
  • This works as long as there are no ngIfs in the table or its parents. Check my answer for details. – Mister Smith Jun 05 '21 at 22:57
12

This table is not very user friendly, and it forces you to manually update, which misses the point of using Angular for the bindings. It is stated in the documentation that:

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.

To call a method on the material table component from the Typescrypt code you need to do it through a ViewChild reference to the table. First add a hashtagged name to the table in the template:

<table #myTable mat-table ... >

Then on your Typescript file, declare a public member with the same name you put after the hashtag in the template, and decorate it with ViewChild so that Angular injects it (I wont be showing the imports):

export class SomeComponent implements OnInit {
    @ViewChild(MatTable) myTable!: MatTable<any>;

(The "!" is needed in new versions of Angular to trick Typescript into believing it will be always non null. Turns out it will. Keep reading)

So now you could do:

this.myTable.renderRows();   

And it would work unless the table or any of the parent is inside an *ngIf directive. When that directive is working, the table is not present in the DOM, and the member annotated with ViewChild will be undefined, so you can't call anything on it. This is not a problem of the material table in particular, it is how Angular is designed. Check this question for solutions. My favourite is to replace the *ngIf with [hidden]. That is ok if the directive was in the table, but becomes messy when it is in the parents.

Mister Smith
  • 27,417
  • 21
  • 110
  • 193
5

@ViewChild('myTable') myTable: MatTableModule

You're not supposed to query for the string. This will query the reference (defined like <cmp #ref>). Also the type is wrong: you're not grabbing a module from the view, you're grabbing a component.

You should import the component you want to query and do the following (change according to which component exactly you need to query):

@ViewChild(MatTable) matTable: MatTable

The argument in the ViewChild decorator is the component you want to query and the type is just for your convenience -- you could omit it or say any, but you won't have any help from TypeScript if you do not do it so it's recommended to leave it.

Lazar Ljubenović
  • 18,976
  • 10
  • 56
  • 91
2

You can use

import {Component, ViewChild} from '@angular/core';
import {MatTable} from '@angular/material';

@ViewChild(MatTable) table: MatTable<any>;

anyFunction(): void {
   this.table.renderRows();
}

As mention on another's answers. Or you can pass the service to application state(@ngrx/store) For example:

import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { Store } from '@ngrx/store';

import * as reducer from '../../app.reducer';

export class Test implements OnInit {
dataSource = new MatTableDataSource<any>();

constructor(private _store: Store<reducer.State>){}

 ngOnInit(): void {
    this._store.select(reducer.getYourSelectorCreated)
      .subscribe(res) => { <<<<--- Subscribe to listen changes on your "table data -state"
        this.dataSource.data = res;  <<-- Set New values to table
      });
    this._someService.fetchYourDataFromStateFunction();  <<-- Service to change the state
  }
}
Luis Fernando
  • 1,146
  • 8
  • 21