Угловой материал 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; 

Не нужно изобретать глубокий доступ к собственности.