Угловой материал 2 DataTable Сортировка с вложенными объектами
У меня есть обычный DataTable Angular Material 2 с заголовками сортировки.
Все виды заголовков работают нормально. За исключением одного с объектом в качестве значения.
Это не сортирует вообще.
Например:
<!-- Project Column - This should sort!-->
<ng-container matColumnDef="project.name">
<mat-header-cell *matHeaderCellDef mat-sort-header> Project Name </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.project.name}} </mat-cell>
</ng-container>
обратите внимание на element.project.name
Вот конфигурация displayColumn:
displayedColumns = ['project.name', 'position', 'name', 'test', 'symbol'];
Изменение 'project.name'
на 'project'
не работает и не "project['name']"
Что мне не хватает? Это вообще возможно?
Вот Stackblitz:
Сортировка объектов Angular Material2 DataTable
Изменить:
Спасибо за все ваши ответы.
У меня уже есть работа с динамическими данными. Поэтому мне не нужно добавлять оператор switch для каждого нового вложенного свойства.
Вот мое решение: (Создание нового источника данных, который расширяет MatTableDataSource, не обязательно)
export class NestedObjectsDataSource extends MatTableDataSource<MyObjectType> {
sortingDataAccessor: ((data: WorkingHours, sortHeaderId: string) => string | number) =
(data: WorkingHours, sortHeaderId: string): string | number => {
let value = null;
if (sortHeaderId.indexOf('.') !== -1) {
const ids = sortHeaderId.split('.');
value = data[ids[0]][ids[1]];
} else {
value = data[sortHeaderId];
}
return _isNumberValue(value) ? Number(value) : value;
}
constructor() {
super();
}
}
Ответы
Ответ 1
Трудно было найти документацию по этому вопросу, но это возможно с помощью sortingDataAccessor
и оператора switch. Например:
@ViewChild(MatSort) sort: MatSort;
ngOnInit() {
this.dataSource = new MatTableDataSource(yourData);
this.dataSource.sortingDataAccessor = (item, property) => {
switch(property) {
case 'project.name': return item.project.name;
default: return item[property];
}
};
this.dataSource.sort = sort;
}
Ответ 2
Вы можете написать функцию в компоненте, чтобы получить глубокое свойство объекта. Затем используйте его в dataSource.sortingDataAccessor
как dataSource.sortingDataAccessor
ниже.
getProperty = (obj, path) => (
path.split('.').reduce((o, p) => o && o[p], obj)
)
ngOnInit() {
this.dataSource = new MatTableDataSource(yourData);
this.dataSource.sortingDataAccessor = (obj, property) => this.getProperty(obj, property);
this.dataSource.sort = sort;
}
columnDefs = [
{name: 'project.name', title: 'Project Name'},
{name: 'position', title: 'Position'},
{name: 'name', title: 'Name'},
{name: 'test', title: 'Test'},
{name: 'symbol', title: 'Symbol'}
];
И в html
<ng-container *ngFor="let col of columnDefs" [matColumnDef]="col.name">
<mat-header-cell *matHeaderCellDef>{{ col.title }}</mat-header-cell>
<mat-cell *matCellDef="let row">
{{ getProperty(row, col.name) }}
</mat-cell>
</ng-container>
Ответ 3
Ответ, как указано, может быть даже сокращен, без необходимости переключения, если вы используете точечную нотацию для полей.
ngOnInit() {
this.dataSource = new MatTableDataSource(yourData);
this.dataSource.sortingDataAccessor = (item, property) => {
if (property.includes('.')) return property.split('.').reduce((o,i)=>o[i], item)
return item[property];
};
this.dataSource.sort = sort;
}
Ответ 4
Я использую общий метод, который позволяет использовать dot.seperated.path с mat-sort-header
или matColumnDef
. Это терпит неудачу, не возвращаясь неопределенно, если не может найти свойство, продиктованное путем.
function pathDataAccessor(item: any, path: string): any {
return path.split('.')
.reduce((accumulator: any, key: string) => {
return accumulator ? accumulator[key] : undefined;
}, item);
}
Вам просто нужно установить доступ к данным
this.dataSource.sortingDataAccessor = pathDataAccessor;
Ответ 5
Я настроил несколько уровней вложенных объектов.
this.dataSource.sortingDataAccessor =
(data: any, sortHeaderId: string): string | number => {
let value = null;
if (sortHeaderId.includes('.')) {
const ids = sortHeaderId.split('.');
value = data;
ids.forEach(function (x) {
value = value? value[x]: null;
});
} else {
value = data[sortHeaderId];
}
return _isNumberValue(value) ? Number(value) : value;
};
Ответ 6
Он пытается сортировать по элементу ['project.name']. Очевидно, что элемент не обладает таким свойством.
Создавать собственный источник данных, который расширяет MatTableDatasource и поддерживать сортировку по свойствам вложенных объектов, должно быть легко. Ознакомьтесь с примерами в файле material.angular.io, используя пользовательский источник.
Ответ 7
У меня была такая же проблема: при тестировании первого предложения у меня были некоторые ошибки, я мог бы исправить это, добавив "switch (property)"
this.dataSource.sortingDataAccessor =(item, property) => {
switch (property) {
case 'project.name': return item.project.name;
default: return item[property];
}
};
Ответ 8
Еще одна альтернатива, которую никто не выкинул сюда, - сначала сгладьте колонку...
yourData.map((d) =>
d.flattenedName = d.project && d.project.name ?
d.project.name :
'Not Specified');
this.dataSource = new MatTableDataSource(yourData);
Просто еще одна альтернатива, плюсы и минусы для каждого!
Ответ 9
Мне нравятся решения @Hieu_Nguyen. Я просто добавлю, что если вы используете lodash в своем проекте так же, как и я, то решение будет таким:
import * as _ from 'lodash';
this.dataSource.sortingDataAccessor = _.get;
Не нужно изобретать глубокий доступ к собственности.