Сортування таблиці даних Angular Material 2 за допомогою вкладених об’єктів


84

У мене є звичайна таблиця даних 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 для кожного нового вкладеного властивості.

Ось моє рішення: (Створення нового DataSource, який розширює 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
Не могли б ви оновити стек-бліц з виправленням
Сатья Рам

Відповіді:


169

Було важко знайти документацію з цього приводу, але це можливо за допомогою 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;
}

7
ти врятував мене. Дякую.
сібас

1
звідки ти взявся sortвthis.dataSource.sort = sort;
Joey Gough

3
Мені довелося помістити це, ngAfterViewInitщоб воно спрацювало
Мел

Це спрацювало добре, просто розмістив його у ngAfterViewInit, як зазначено у коментарі вище.
Wael,

помістив це поруч із моєю декларацією таблиці, і вона зразу спрацювала. Врятував мені тонну налагодження. Дякую!
JamieT

31

Ви можете написати функцію в компоненті, щоб глибоко отримати властивість від об'єкта. Потім використовуйте його, 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>

1
Здається, це найкраще рішення, невелике та стисле, і воно не таке обмежене, як перемикач.
Івар Калєярв,

Мені дуже подобається ця реалізація. Вирізає код, який потрібно використовувати / генерувати. Я зіткнувся з проблемою з останньою реалізацією матових таблиць з цим раніше, оновлення викликали проблеми. Однак це чисто.
LP

3
Мені теж подобаються ці рішення. Я використовую lodashу своєму проекті, тому, якщо ви використовуєте lodash, це рішення означає: this.dataSource.sortingDataAccessor = _.get;Не потрібно винаходити глибокий доступ до властивостей.
Енді,

2
@andy, ви повинні зробити це окремою відповіддю. це звучить занадто просто, щоб бути правдивим у коментарі .. Це все, що мені потрібно зробити?
Simon_Weaver

11

Наведену відповідь можна навіть скоротити, перемикач не потрібен, якщо ви використовуєте крапкові позначення для полів.

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;
}

5

Мені подобаються рішення @Hieu_Nguyen. Я просто додам, що якщо ви використовуєте lodash у своєму проекті, як і я, тоді рішення перекладається на це:

import * as _ from 'lodash';

this.dataSource.sortingDataAccessor = _.get; 

Не потрібно винаходити глибокий доступ до власності.


1
Працює чудово, але для тих, хто бореться: вам слід назвати displayedColumns'шлях' до значень, тобто, ['title', 'value', 'user.name'];а потім використовувати <ng-container matColumnDef="user.name">у вашому шаблоні.
Джеффрі Розендал,

Крім того, ви можете залишити імена стовпців як є і перевизначити sortHeaderId самостійно, mat-sort-headerнаприклад,mat-sort-header="user.name"
p4m

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;

1

Я налаштував для кількох вкладених об’єктів.

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;
  };

Ваше рішення допомогло мені найбільше, оскільки я зрозумів, що можу повернути номер або рядок. У моїй таблиці є обидва типи, і їх потрібно було сортувати там, де числа були відсортовані чисельно, а не як рядки. Ключовим фактором рішення було використання тернарного оператора, який перевіряє набір тексту.
TYMG

Я зрозумів Cannot find name '_isNumbervalue, і припускаючи, що це метод lodash, я не можу знайти метод у модулі вузла. isNumberіснує. Я раніше не був знайомий з лодашем, якщо це те, що це таке. Як я цим користуюся?
Рін і Лен

1
імпортувати {_isNumberValue} з "@ angular / cdk / coercion";
Е.Саравут,

1

Ще одна альтернатива, яку сюди ніхто не викинув, спочатку вирівняйте колону ...

yourData.map((d) => 
   d.flattenedName = d.project && d.project.name ? 
                     d.project.name : 
                     'Not Specified');

this.dataSource = new MatTableDataSource(yourData);

Просто ще одна альтернатива, плюси і мінуси для кожного!


1

Просто додайте це у своє джерело даних, і ви зможете отримати доступ до вкладеного об’єкта

this.dataSource.sortingDataAccessor = (item, property) => {
    // Split '.' to allow accessing property of nested object
    if (property.includes('.')) {
        const accessor = property.split('.');
        let value: any = item;
        accessor.forEach((a) => {
            value = value[a];
        });
        return value;
    }
    // Access as normal
    return item[property];
};

0

Він намагається сортувати за елементом ['project.name']. Очевидно, що елемент не має такої властивості.

Створити власний джерело даних, що розширює MatTableDatasource і підтримує сортування за властивостями вкладених об’єктів, має бути легко. Ознайомтеся з прикладами в документах material.angular.io щодо користувацького джерела.


0

У мене була та сама проблема, протестувавши першу пропозицію, у мене були помилки, я міг це виправити, додавши "перемикач (властивість)"

this.dataSource.sortingDataAccessor =(item, property) => {
    switch (property) {
    case 'project.name': return item.project.name;

    default: return item[property];
    }
  };

0

Використовуйте MatTableDataSource Check повне рішення проблеми MatSort

в HTML

    <ng-container matColumnDef="createdDate" @bounceInLeft>
      <th mat-header-cell *matHeaderCellDef mat-sort-header class="date"> Created date
      </th>
          <td mat-cell *matCellDef="let element" class="date"> {{element.createdDate
           | date :'mediumDate'}} </td>
   </ng-container>

  <ng-container matColumnDef="group.name">
    <th mat-header-cell *matHeaderCellDef mat-sort-header class="type"> Group </th>
    <td mat-cell *matCellDef="let element" class="type"> {{element.group.name}} </td>
  </ng-container>

@ViewChild(MatSort, { static: true }) 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;
}

0

Якщо ви хочете мати таблицю Angular material з деякими розширеними функціями, наприклад, сортування для вкладених об’єктів, перегляньте https://github.com/mikelgo/ngx-mat-table-extensions/blob/master/libs/ngx-mat -таблиця / README.md .

Я створив цю бібліотеку, оскільки мені не вистачало деяких особливостей матового столу.

Розширене сортування подібне до запропонованої відповіді @Hieu Nguyen, але трохи розширене, щоб також мати належне сортування за великими та малими літерами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.