Tell me why the sorting in the table does not work, I do everything according to the example from the official documentation of Angular Material

Template code:

<h5 class="card-title">****************</h5> <table mat-table [dataSource]="tabledata" matSort *ngIf="isLoaded"> <ng-container matColumnDef="Ѐамилия, имя, отчСство"> <th mat-header-cell *matHeaderCellDef mat-sort-header> Ѐамилия, имя, отчСство </th> <td mat-cell *matCellDef="let element"> {{element.name}} </td> </ng-container> <ng-container matColumnDef="скилл P"> <th mat-header-cell *matHeaderCellDef mat-sort-header> скилл P </th> <td mat-cell *matCellDef="let element"> {{element.skillP}} </td> </ng-container> <ng-container matColumnDef="скилл A"> <th mat-header-cell *matHeaderCellDef mat-sort-header> скилл A </th> <td mat-cell *matCellDef="let element"> {{element.skillA}} </td> </ng-container> <ng-container matColumnDef="скилл E"> <th mat-header-cell *matHeaderCellDef mat-sort-header> скилл E </th> <td mat-cell *matCellDef="let element"> {{element.skillE}} </td> </ng-container> <ng-container matColumnDef="скилл I"> <th mat-header-cell *matHeaderCellDef mat-sort-header> скилл I </th> <td mat-cell *matCellDef="let element"> {{element.skillI}} </td> </ng-container> <ng-container matColumnDef="ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅"> <th mat-header-cell *matHeaderCellDef mat-sort-header> ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ </th> <td mat-cell *matCellDef="let element"> {{element.department}} </td> </ng-container> <ng-container matColumnDef="Π”ΠΎΠ»ΠΆΠ½ΠΎΡΡ‚ΡŒ"> <th mat-header-cell *matHeaderCellDef> Π”ΠΎΠ»ΠΆΠ½ΠΎΡΡ‚ΡŒ </th> <td mat-cell *matCellDef="let element"> {{element.position}} </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> 

And here is the component code:

 import { Component, OnInit, ViewChild } from '@angular/core'; import { Subscription, combineLatest } from 'rxjs'; import { PersonsService } from 'src/app/shared/services/persons.service'; import { PaeiService } from 'src/app/shared/services/paei.service'; import { Paei } from 'src/app/assessment/paei-test/models/paei.model'; import { Person } from 'src/app/shared/models/person.model'; import { MatTableDataSource, MatSort } from '@angular/material'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'mvv-paei-table', templateUrl: './paei-table.component.html', styleUrls: ['./paei-table.component.scss'] }) export class PaeiTableComponent implements OnInit { sub: Subscription; personsList: Person[] = []; paeiList: Paei[] = []; persons: { name: string, skillP: number, skillA: number, skillE: number, skillI: number, department: string, position: string }[] = []; tabledata: any; public displayedColumns = [ 'Ѐамилия, имя, отчСство', 'скилл P', 'скилл A', 'скилл E', 'скилл I', 'ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅', 'Π”ΠΎΠ»ΠΆΠ½ΠΎΡΡ‚ΡŒ' ]; isLoaded = false; constructor( private personsService: PersonsService, private paeiService: PaeiService, ) { } @ViewChild(MatSort) sort: MatSort; ngOnInit() { this.sub = combineLatest( this.personsService.getPersons(), this.paeiService.getAllPaei() ).subscribe(value => { value[0].forEach(doc => { const person = new Person( doc.data().name, doc.data().middleName, doc.data().surname, doc.data().gender, doc.data().date, doc.data().position, doc.data().department, doc.id ); this.personsList.push(person); }); value[1].forEach(doc => { const paei = new Paei( doc.data().id, doc.data().date, doc.data().skillP, doc.data().skillA, doc.data().skillE, doc.data().skillI, doc.data().isReaded ); this.paeiList.push(paei); }); for (let i = 0; i < this.paeiList.length; i++) { const pers = this.personsList.filter( res => res.personId === this.paeiList[i].id); const name = pers[0].surname + ' ' + pers[0].name + ' ' + pers[0].middleName; this.persons.push( { name: name, skillP: this.paeiList[i].skillP, skillA: this.paeiList[i].skillA, skillE: this.paeiList[i].skillE, skillI: this.paeiList[i].skillI, department: pers[0].department, position: pers[0].position } ); this.tabledata = new MatTableDataSource(this.persons); } this.isLoaded = true; setTimeout(() => { this.tabledata.sort = this.sort; console.log('ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° сортировки', this.sort); }, 1000); }); } } 

Do not pay attention to the timeout.

    1 answer 1

    You do not quite do everything by example, because you are using the ngIf directive πŸ™‚

    I will assume that you imported MatSortModule , so I will immediately describe your problem.

    When you use the @ViewChild decorator, you tell the compiler to generate a specific data structure called NodeDef (node ​​definition). In Angular runtime, it’s not just building a DOM tree, and also setting all the necessary class properties for you that you decorate with @Input() | @ViewChild() | @HostBinding() @Input() | @ViewChild() | @HostBinding() @Input() | @ViewChild() | @HostBinding() and so on.

    In your example, during the first change detection mechanism, Angular calls the checkAndUpdateQuery function, which sets the properties decorated with @ViewChild() | @ViewChildren() | @ContentChild() | @ContentChildren() @ViewChild() | @ViewChildren() | @ContentChild() | @ContentChildren() @ViewChild() | @ViewChildren() | @ContentChild() | @ContentChildren() . The problem is that the table element is wrapped in ng-template (due to the use of the ngIf directive), and isLoaded is false at the initial stage, so Angular cannot physically access the instance of the MatSort directive (because it has not been created yet). ) and register the property. In addition, the table element itself is asynchronous to the DOM.

    In order to make sure of this - output to the sort console in the ngOnInit hook:

     ngOnInit() { console.log(this.sort); // undefined } 

    There are 2 ways to solve this problem:

    1) Use the hidden attribute instead of the ngIf directive:

     <table [hidden]="!isLoaded"> ... </table> 

    2) Inject a reference to the view into the constructor and start the mechanism for detecting changes after setting the property isLoaded = true , which in turn will again call the checkAndUpdateQuery function, which pops up the sort property:

     constructor(private ref: ChangeDetectorRef) {} ngOnInit() { combineLatest(...).subscribe(() => { ... this.isLoaded = true; this.ref.detectChanges(); console.log(this.sort); this.tableDataSource.sort = this.sort; }); } 
    • Yes, it helped. But only the first column is sorted. At first, I didn’t even understand what was working, because I tried to sort the numeric columns - Vyacheslav
    • But I figured it out, there were problems with different data names - Vyacheslav