Angular Material 2 DataTable对嵌套对象进行排序


82

我有一个带有排序标题的普通Angular Material 2 DataTable。各种各样的头工作正常。除了以对象为值的对象。这些根本不排序。

例如:

 <!-- 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
您能否使用修复程序更新堆叠闪电战
萨蒂亚·拉姆

Answers:


164

很难找到关于此的文档,但是可以通过使用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
你救了我。谢谢。
鲈鱼

你从哪里弄来sortthis.dataSource.sort = sort;
乔伊·高夫

3
我必须放置ngAfterViewInit它才能使其正常工作
梅尔(Mel)

效果很好,只需将其放在上面评论中提到的ngAfterViewInit中即可。
Wael

将其放在我的表声明旁边,即可立即工作。节省了我很多调试时间。谢谢!
JamieT

29

您可以在组件中编写一个函数,以从对象中深入了解属性。然后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
这似乎是最好的解决方案,它既小巧又简洁,并且不像开关那样受限制。
IvarKallejärv'18

我真的很喜欢这个实现。减少必须使用/生成的代码。之前我在使用mat表的最后一个实现时遇到了问题,刷新引起了问题。这是干净的。
LP

3
我也喜欢这种解决方案。我lodash在我的项目中使用过lodash,因此如果您使用,则此解决方案可以转换为:this.dataSource.sortingDataAccessor = _.get;无需重新发明深层属性访问。
安迪

1
@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">在模板中使用。
Jeffrey Roosendaal

4

我使用一种通用方法,该方法允许您将dot.seperated.path与mat-sort-header或一起使用matColumnDef。如果找不到路径所指定的属性,则无提示地返回undefined会失败。

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方法,但在node模块中找不到该方法。isNumber存在。如果以前就是这样,我以前并不熟悉lodash。我该如何使用?
Rin和Len

1
从“ @ angular / cdk / coercion”导入{_isNumberValue};
E.Sarawut,

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

1
非常感谢你!这对我来说很完美。
Paco Zevallos

0

它正在尝试按element ['project.name']排序。显然element没有这样的属性。

创建扩展MatTableDatasource并支持按嵌套对象属性排序的自定义数据源应该很容易。请查看material.angular.io文档中有关使用自定义源的示例。


0

我遇到了同样的问题,通过测试第一个命题我有一些错误,我可以通过添加“ switch(property)”来修复它

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

    default: return item[property];
    }
  };

0

使用MatTableDataSource 检查完整的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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.