Mike Varela
Mike Varela

Reputation: 527

Angular Material Data Table - Can't Sort

Trying to sort my data and for some reason it's not working. All examples use a local array of values, while mine is an API call which returns an array of objects, of which, each item has a 'name' property.

I can load the page and see the sort header arrow but clicking doesn't produce a change. I'm using a function call to get my 'clients' and calling that in the constructor, but it's failed for me in both OnInit as well. I have also checked that my column def is identical to my data field returned, namely 'name' but that's not helping either.

------ TS ---------

import { Component, OnInit, ViewChild } from "@angular/core";
import { Router } from "@angular/router";

import { ClientService } from "../../../core/services/client.service";
import { Client } from "../../../core/models/client.interface";
import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
import { ClientDialogComponent } from "../client-dialog/client-dialog.component";
import { ConfirmDeleteDialogComponent } from "../../../core/components/confirm-delete-dialog/confirm-delete-dialog.component";
import { SnackbarService } from "src/app/core/services/snackbar.service";
import { MatTableDataSource } from "@angular/material/table";
import { MatSort } from "@angular/material/sort";

@Component({
  templateUrl: "./clients.component.html",
  styleUrls: ["./clients.component.css"],
})
export class ClientsComponent implements OnInit {
  dataSource: MatTableDataSource<Client[]>;
  client: Client;
  tableColumns: string[] = ["name", "website", "phone", "actions"];

  @ViewChild(MatSort) sort: MatSort;

  constructor(
    private _clientService: ClientService,
    private _router: Router,
    private _snackbar: SnackbarService,
    public dialog: MatDialog
  ) {
    this.getClients();
  }

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

  getClients() {
    // Get clients from API
    this._clientService.getClients().subscribe(
      (response) => {
        this.dataSource = new MatTableDataSource(response);   <----- this is simple array of objects
      },
      (error) => {
        this._snackbar.show(error["message"], "warn-snackbar");
      }
    );
  }
}

-------- HTML --------------

<div *ngIf="dataSource; else spinner">
  <div class="row">
    <div class="search">
      <mat-form-field>
        <input type="text" matInput placeholder="Filter" (keyup)="applyFilter($event.target.value)">
      </mat-form-field>
    </div>
    <div class="col-button">
      <button *hasClaim="'canAddClient'" type="button" mat-stroked-button color="primary" class="add-client-button"
        (click)="addClientDialog()">
        Add Client
      </button>
    </div>
  </div>
  <div class="row">
    <div class="col-table">
      <table mat-table [dataSource]="dataSource" matSort>
        <!-- row / column definitions -->
        <tr mat-header-row *matHeaderRowDef="tableColumns" [ngClass]="'table-header'"></tr>
        <tr mat-row *matRowDef="let row; columns: tableColumns">
        </tr>
        <!-- client -->
        <ng-container matColumnDef="name">
          <th mat-header-cell *matHeaderCellDef mat-sort-header>Client</th>
          <td mat-cell *matCellDef="let client"> {{client.name}} </td>
        </ng-container>
        <!-- website -->
        <ng-container matColumnDef="website">
          <th mat-header-cell *matHeaderCellDef>Website</th>
          <td mat-cell *matCellDef="let client"> {{client.website}} </td>
        </ng-container>
        <!-- phone -->
        <ng-container matColumnDef="phone">
          <th mat-header-cell *matHeaderCellDef>Phone</th>
          <td mat-cell *matCellDef="let client"> {{client.phone | formatPhone}} </td>
        </ng-container>
        <!-- actions -->
        <ng-container matColumnDef="actions">
          <th mat-header-cell *matHeaderCellDef>Actions</th>
          <td mat-cell *matCellDef="let client">
            <button mat-stroked-button class="action-button" color="primary" (click)="goToDetailsPage(client._id)">
              Info
            </button>
            <button *hasClaim="'canEditClient'" mat-stroked-button class="action-button" color="primary"
              (click)="editClientDialog(client)">
              Edit
            </button>
            <button *hasClaim="'canDeleteClient'" mat-stroked-button class="action-button" color="warn"
              (click)="deleteClientDialog(client)">
              Delete
            </button>
          </td>
        </ng-container>
      </table>
    </div>
  </div>
</div>

<ng-template #spinner>
  <mat-spinner></mat-spinner>
</ng-template>

Upvotes: 0

Views: 97

Answers (2)

ernematthew
ernematthew

Reputation: 403

I think your issue is happening because you are setting the sort on the dataSource before the ViewChild is defined. There are two ways that should fix this:

  1. Add {static: true} to your @ViewChild definition as below:

    @ViewChild(MatSort, {static: true}) sort: MatSort;
    

    This causes the Angular to eagerly fetch the MatSort element, so it is available in ngOnInit.

  2. Move the sort assignment into ngAfterViewInit. In the code you provided, you can rename the ngOnInit function to ngAfterViewInit. This works because non-static ViewChild objects are available in ngAfterViewInit.

If you're interested, you can read up on the difference between ngOnInit and ngAfterViewInit and the static flag to ViewChild.

Upvotes: 1

Saurav Ahlawat
Saurav Ahlawat

Reputation: 81

Why don't you create your table from CLI ? It packs a lot of functionality by default and saves a lot of time as well.

There you can hit your API and store the data, pretty straight forward process. For your current table, I don't know why it's not working properly

Upvotes: 0

Related Questions