angularangular-materialtablesort

Angular material table does not work when sort + @Input()


I´m doing a table component.

This component works perfectly without sortable functionnality. When I´m trying to make the table sortable, that works if I´m using basic example when you store the object list in a constant but rows do not display when I´m trying to use @Input()

Version with constant - That works

import {AfterContentInit, AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
import {Client, ClientStatus} from '@models/client.model';
import {ClientService} from '@services/client.service';
import {MatSort, Sort} from '@angular/material/sort';
import {MatTableDataSource} from '@angular/material/table';

const ELEMENT_DATA: Client[] = [
 ...
];

@Component({
  selector: 'app-table-entries',
  templateUrl: './table-entries.component.html',
  styleUrls: ['./table-entries.component.scss']
})
export class TableEntriesComponent implements OnInit, AfterViewInit {
  displayedColumns: string[] = ['expectedDate', 'description.id', 'priority' ];
  @ViewChild(MatSort, {static: true}) sort: MatSort;
  sortableEntries = new MatTableDataSource(ELEMENT_DATA);

  constructor( clientService: ClientService ) {}
  ngOnInit(): void {}

  ngAfterViewInit() {
    this.sort.active = 'expectedDate';
    this.sort.direction = 'asc';
    this.sortableEntries.sort = this.sort;
    this.sortableEntries.sortingDataAccessor = ( client, property) => {
      switch ( property ) {
        case 'description.id': return client.description.id;
        default: return client[property];
      }
    };
  }
}

Version with @Input - That does not work

import {AfterContentInit, AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
import {Client, ClientStatus} from '@models/client.model';
import {ClientService} from '@services/client.service';
import {MatSort, Sort} from '@angular/material/sort';
import {MatTableDataSource} from '@angular/material/table';

@Component({
  selector: 'app-table-entries',
  templateUrl: './table-entries.component.html',
  styleUrls: ['./table-entries.component.scss']
})
export class TableEntriesComponent implements OnInit, AfterViewInit {
  @Input() entries: Client[] = [];
  displayedColumns: string[] = ['expectedDate', 'description.id', 'priority' ];
  @ViewChild(MatSort, {static: true}) sort: MatSort;
  sortableEntries: MatTableDataSource<Client>;

  constructor( clientService: ClientService ) {}
  ngOnInit(): void {
      this.sortableEntries = new MatTableDataSource<Client>( this.entries );
  }

  ngAfterViewInit() {
    this.sort.active = 'expectedDate';
    this.sort.direction = 'asc';
    this.sortableEntries.sort = this.sort;
    this.sortableEntries.sortingDataAccessor = ( client, property) => {
      switch ( property ) {
        case 'description.id': return client.description.id;
        default: return client[property];
      }
    };
  }
}

Template

<table mat-table matSort [dataSource]="sortableEntries">
  <!-- Heure RDV -->
  <ng-container matColumnDef="expectedDate">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Heure rdv </th>
    <td mat-cell *matCellDef="let element"> {{ element.expectedDate | date: "d/M HH:mm" }} </td>
  </ng-container>

  <!-- Identifiant -->
  <ng-container matColumnDef="description.id">
    <th mat-header-cell *matHeaderCellDef mat-sort-header="description.id"> Identifiant </th>
    <td mat-cell *matCellDef="let element"> {{ element.description.id}} </td>
  </ng-container>

  <!-- Nature -->
  <ng-container matColumnDef="priority">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Nature </th>
    <td mat-cell *matCellDef="let element" > {{ element.priority | getStringPriority }} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
  <tr mat-row *matRowDef="let row; let even = even; columns: displayedColumns;" [ngClass]="{ 'table-even-row': even}"></tr>
</table>
<section *ngIf="entries && entries.length === 0" class="no-client-wrapper"><p>Il n'y a pas d'entrée actuellement</p></section>

I can imagine the problem is the moment when the object list is loaded but I don´t know how to fix this. Someone has got an idea ?


Solution

  • I found a solution here.

    First of all, change @Input() entries: Client[] = []; by

    @Input() set entries( data: Client[] ) {
      this.setTableDataSource(data);
    }
    

    Then, Use the content in ngAfterViewInit to make the function

    setTableDataSource(data: any) {
        this.sortableEntries = new MatTableDataSource<any>(data);
        this.sortableEntries.sort = this.sort;
        this.sort.active = 'expectedDate';
        this.sort.direction = 'asc';
        this.sortableEntries.sortingDataAccessor = ( client, property) => {
          switch ( property ) {
            case 'description.codeUT': return client.description.codeUT;
            default: return client[property];
          }
        };
      }
    

    Complete result

    import {AfterContentInit, AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
    import {Client, ClientStatus} from '@models/client.model';
    import {ClientService} from '@services/client.service';
    import {MatSort, Sort} from '@angular/material/sort';
    import {MatTableDataSource} from '@angular/material/table';
    
    @Component({
      selector: 'app-table-entries',
      templateUrl: './table-entries.component.html',
      styleUrls: ['./table-entries.component.scss']
    })
    export class TableEntriesComponent implements OnInit{
      displayedColumns: string[] = ['expectedDate', 'description.id', 'priority' ];
      @ViewChild(MatSort, {static: true}) sort: MatSort;
      sortableEntries: MatTableDataSource<Client>;
    
      @Input() set entries( data: Client[] ) {
        this.setTableDataSource(data);
      }
    
      setTableDataSource(data: any) {
        this.sortableEntries = new MatTableDataSource<any>(data);
        this.sortableEntries.sort = this.sort;
        this.sort.active = 'expectedDate';
        this.sort.direction = 'asc';
        this.sortableEntries.sortingDataAccessor = ( client, property) => {
          switch ( property ) {
            case 'description.id': return client.description.id;
            default: return client[property];
          }
        };
      }
    
      constructor( clientService: ClientService ) {
        this.sortableEntries = new MatTableDataSource<Client>( this.entries );
      }
      ngOnInit(): void {
        console.dir( this.entries );
      }
    
    }