Angular Material Table: sort complex objects using MatTableDataSource
- The simple scenario, mapping and sorting out of the box
- The complex scenario, a structured object with nested properties
The simple scenario, mapping and sorting out of the box
In this example we will show how to sort complex objects in an Angular Material Table.
When we use MatTableDataSource
in Angular Material, Angular out of the box matches the columns with the property of our object.
Example, we want to show a list of students:
The student is a simple object with 2 properties: name and age.
export interface Student {
name: string;
age: number;
}
In the Angular component we retrieve a list of students from a service and we assign them to the DataSource:
@ViewChild(MatSort) sort!: MatSort;
displayedColumns: string[] = ['name', 'age'];
dataSource: MatTableDataSource<Student> = new MatTableDataSource(students);
private initTable(): void {
this.service.loadStudents().pipe(
first(),
map(datasource => {
this.dataSource.data = datasource;
this.dataSource.sort = this.sort;
})
).subscribe();
}
Our HTML table can manage the sorting for us without any configuration.
Angular maps the properties of the object student
with the columns name
and age
.
<table mat-table [dataSource]="dataSource" [trackBy]="..."]>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="age">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Age </th>
<td mat-cell *matCellDef="let element"> {{element.age}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns; let i = index;"></tr>
</table>
The complex scenario, a structured object with nested properties
If our object is more complex Angular cannot automatically map the properties with the columns.
Example:
export interface Student {
name: string;
age: number;
address: Address;
}
export interface Address {
street: string;
city: string;
}
In this case we have to define a SortingDataAccessor to tell Angular which field has to return for a given column name.
From the documentation:
sortingDataAccessor: (data: T, sortHeaderId: string) => string | number
Data accessor function that is used for accessing data properties for sorting through the default sortData function. This default function assumes that the sort header IDs (which defaults to the column name) matches the data's properties (e.g. column Xyz represents data['Xyz']). May be set to a custom function for different behavior.
In our component we have to define a sortingDataAccessor
function:
sortingDataAccessor: (data: Student, property: string) : string | number => {
switch (property) {
case 'name': return data.name;
case 'age': return data.age;
case 'street': return data.address.street;
case 'city': return data.address.city;
default: return data[property];
}
}
Our displayedColumns
array becomes: displayedColumns: ['name', 'age', 'street', 'city']
During the initialization of the table we have to assign the sortingDataAccessor
:
this.dataSource.sortingDataAccessor = this.sortingDataAccessor;
The HTML as to be updated too with the new columns.
Now your 2 dimensional table can represent a list of nested objects.