0

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>

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

That was it. Moved to after view init. Would I also need to add static as well
Either approach works, but you don't need to do both. ViewChilds not marked static are not defined in ngOnInit, so you need to use a later lifecycle hook or mark it as static.
0

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

2 Comments

Thanks, thats a good point. I'm trying to go through myself in the beginning to learn it, then I'll take your suggestion, might be easier
this.dataSource.sort = this.sort; this line should be after this.dataSource

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.