YBO
YBO

Reputation: 51

Angular Material mat-table data not refreshed from parent changes

I am using in a component a mat-table with a MatTableDataSource and I want to set up the data from its parent component but data are not refreshed at screen whereas child component well detects data changes...

Child component

@Component({
  selector: 'app-person-list',
  template: `
    <mat-table #table [dataSource]="dataSource">
      <ng-container matColumnDef="name">
        <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
        <mat-cell *matCellDef="let person">{{ person.name }}</mat-cell>
      </ng-container>
      <ng-container matColumnDef="age">
        <mat-header-cell *matHeaderCellDef> Age </mat-header-cell>
        <mat-cell *matCellDef="let person">{{ person.age }}</mat-cell>
      </ng-container>

      <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
      <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
    </mat-table>
  `
})
export class PersonListComponent implements OnInit, OnChanges {
  @Input() data: Person[];

  displayedColumns = ['name', 'age'];
  dataSource = new MatTableDataSource<Person>();

  ngOnInit() {
    this.dataSource.data = this.data;
  }

  ngOnChanges(changes) {
    console.log('Data changed', this.data);
  }
}

Parent component

@Component({
  selector: 'app',
  template: `<app-person-list [data]="personList"></app-person-list>`
})
export class AppComponent implements OnInit {
  personList: Person[] = [];

  constructor(private service: PersonService) {
  }

  ngOnInit() {
    this.service.getPersonList().subscribe(res => this.personList.push(...res));
  }
}

Plunker

Does someone knows why the table is not updated when data changes ? Thanks in advance!

Upvotes: 2

Views: 4550

Answers (2)

Ivar Kallej&#228;rv
Ivar Kallej&#228;rv

Reputation: 106

The problem is that you're calling this.dataSource.data = this.data; only on ngOnInit, which means that you aren't doing anything with the updated data.

I created a pipe just for this, and so that I wouldn't have to use MatTableDataSource everywhere -

@Pipe({
   name: 'dataSource'
})
export class DataSourcePipe implements PipeTransform {
  transform(array: any[]): any {
    return array ? new MatTableDataSource(array) : new MatTableDataSource([]);
  }
}

Using it like this $data | dataSource in the template

That way your data is always up to date and you don't have to use MatTableDataSource manually.

EDIT: You can still use filter and sort functionalities when using this.

You're giving data to the component and then using MatTableDataSource to wrap it. Instead you can do it like this - <component [data]="$array | async | dataSource"></component> where $array is an observable.

And then inside the component - <mat-table [dataSource]="data"></mat-table>

This way you can just manipulate the data object, without having to wrap it in the MatTableDataSource inside the component.

Upvotes: 4

YBO
YBO

Reputation: 51

The only way I found is to change the reference of the array and reassign it...

Child component

@Component( [...] )
export class PersonListComponent implements OnChanges {
  @Input() data: Person[];

  displayedColumns = ['name', 'age'];
  dataSource = new MatTableDataSource<Person>();

  ngOnChanges(changes) {
    this.dataSource.data = this.data;
  }
}

Parent component

@Component( [...] )
export class AppComponent implements OnInit {
  personList: Person[] = [];

  constructor(private service: PersonService) {
  }

  ngOnInit() {
    this.service.getPersonList().subscribe(res => {
      if (this.personList.length > 0) {
        res.unshift(this.personList);
      }
      this.personList = res;
    });
  }
}

Upvotes: 3

Related Questions