1

Implementing AngularMaterial Table with sorting headers I'm getting this Error in console:

ERROR TypeError: Cannot set property 'sort' of undefined at

and this Error in Terminal:

error TS2740: Type 'MatTableDataSource' is missing the following properties from type 'Employee[]': length, pop, push, concat, and 24 more.

error TS2322: Type 'MatSort' is not assignable to type '(compareFn?: (a: Employee, b: Employee) => number) => Employee[]'.

Type 'MatSort' provides no match for the signature '(compareFn?: (a: Employee, b: Employee) => number): Employee[]'.

I cannot get what it wants from me. I would appreciate any hints!

I've used the code from AngularMaterial Documentation except for an interface for Data. Can this be an issue?

EmployeeListComponent


import { Component, OnInit, ViewChild } from '@angular/core';
import { MatSort, MatTableDataSource } from '@angular/material';
import { AngularFirestore } from '@angular/fire/firestore';
import { EmployeeService } from '../shared/employee.service';
import { Employee } from '../shared/employee';

@Component({
  selector: 'app-employee-list',
  templateUrl: './employee-list.component.html',
  styleUrls: ['./employee-list.component.scss']
})
export class EmployeeListComponent implements OnInit {

  displayedColumns: string[] = ['fullname', 'position', 'mobile'];
  dataSource;

  @ViewChild(MatSort) sort: MatSort;

  constructor(private service: EmployeeService) { }

  ngOnInit() {
    this.service.getEmployees().subscribe(employees => {
      this.dataSource = new MatTableDataSource(employees);
      console.log(this.dataSource);
    });
    this.dataSource.sort = this.sort;

  }

}


EmployeeList HTML


<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">

  <!-- Position Column -->
  <ng-container matColumnDef="fullname">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Full Name </th>
    <td mat-cell *matCellDef="let element"> {{element.fullname}} </td>
  </ng-container>

  <!-- Name Column -->
  <ng-container matColumnDef="position">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Position </th>
    <td mat-cell *matCellDef="let element"> {{element.position}} </td>
  </ng-container>

  <!-- Weight Column -->
  <ng-container matColumnDef="mobile">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Mobile </th>
    <td mat-cell *matCellDef="let element"> {{element.mobile}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>


EmployeeService


getEmployees() {
    return this.firestore.collection<Employee>('employees')
      .snapshotChanges()
      .pipe(
        map(actions => actions.map(a => {
          const data = a.payload.doc.data();
          const id = a.payload.doc.id;
          return { id, ...data } as Employee;
       })
        )
      );
}

Employee Class


export class Employee {
  id: string;
  fullname: string;
  empCode: string;
  position: string;
  mobile: string;
}


  • You can try setting a timeout for the sort operation till the data is fetched, by adding `setTimeout(() => this.dataSource.sort= this.sort);` inside the **ngOnInit** after the **console.log** line. – Karthik_Siddh May 18 '19 at 11:03
  • Did the `setTimeout()` work.? – Karthik_Siddh May 18 '19 at 11:35
  • 1
    @Karthik_Siddh I've solved the problem with _ngAfterViewInit_ and moved some code into a subscribe section. It worked for me well what solved the problem with Errors but my sorting still doesn't work for some reasons. Maybe you know why? I would appreciate any hints on that –  May 18 '19 at 11:41

2 Answers2

1

You are trying to access data from outside the subscription, it should be inside.

After this you will run into trouble with this.sort being undefined in within the ngOnInit hook, this occurs because you are trying to access a HTMLelement that has not yet been draw. (MatSort)

There is a lifecycle hook you can use to ensure that the view is ready to be accessed by the @ViewChild, this is ngAfterViewInit, only is only fired when the HTML is drawn.

Try structuring like so.


import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { MatSort, MatTableDataSource } from '@angular/material';
import { AngularFirestore } from '@angular/fire/firestore';
import { EmployeeService } from '../shared/employee.service';
import { Employee } from '../shared/employee';

@Component({
  selector: 'app-employee-list',
  templateUrl: './employee-list.component.html',
  styleUrls: ['./employee-list.component.scss']
})
export class EmployeeListComponent implements AfterViewInit{

  displayedColumns: string[] = ['fullname', 'position', 'mobile'];
  dataSource;

  @ViewChild(MatSort) sort: MatSort;

  constructor(private service: EmployeeService) { }

  ngAfterViewInit(): void
  {
    this.service.getEmployees().subscribe(employees => {
      this.dataSource = new MatTableDataSource(employees);
      if (this.sort) // check it is defined.
      {
          this.dataSource.sort = this.sort;
      }
    });
  }

}

Documentation AfterViewInit Lifecycle // More details.

devDan
  • 5,969
  • 3
  • 21
  • 40
  • Thanks! It helped with the Error but my sorting still doesn’t work :/ I’ve already compared everything to the original code from the documentation - everything is the same. What can be a problem? –  May 18 '19 at 11:15
  • im not 100% with material bits just offering my help with the more angular related errors. Does you data match what the mat sort is expecting ? – devDan May 18 '19 at 11:18
  • I guess so it builds up my table with the data I have. AngularMaterial requires an Array with data. I’ve got it. Can the reason be of using an Employee class except for Employee interface? That’s the only difference which I can see. I’ve used all the tags matSort on mat-table, and may-sort-header on mat-header-cell –  May 18 '19 at 11:25
  • please edit in your data to the question and the shape of the class you have defined will take a look – devDan May 18 '19 at 11:32
  • My bad! :D I should have imported MatSortModule into my module. I've examined the documentation. Thanks again! –  May 18 '19 at 12:51
0

Overall you code looks Okay, but this line

this.dataSource.sort = this.sort;

is in wrong place , it should be placed inside subscribe block.

Reason for ERROR TypeError: Cannot set property 'sort' of undefined

You are mxing your synchronous code with asynchronous and by that I mean that your subscribe code will only execute when the response from API is returned (1 to 2 sec) but this.dataSource.sort = this.sort; will not wait for response because it's synchronous code and it'll continue its execution regardless of any HTTP request and your dataSource object will be undefined so you need to shift it in asynchronous part.

Your code would be like this:

ngOnInit() {
    this.service.getEmployees().subscribe(employees => {
      this.dataSource = new MatTableDataSource(employees);
      console.log(this.dataSource);
      this.dataSource.sort = this.sort; <- note this line 

   });   
}
asimhashmi
  • 4,258
  • 1
  • 14
  • 34