Angular Material 2 DataTable 使用嵌套对象排序

Posted

技术标签:

【中文标题】Angular Material 2 DataTable 使用嵌套对象排序【英文标题】:Angular Material 2 DataTable Sorting with nested objects 【发布时间】:2018-07-31 03:56:35 【问题描述】:

我有一个带有排序标题的普通 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 sort objects

编辑: 感谢您的所有回答。 我已经让它处理动态数据了。所以我不必为每个新的嵌套属性添加 switch 语句。

这是我的解决方案:(不需要创建扩展 MatTableDataSource 的新 DataSource)

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();
  

【问题讨论】:

能否请您更新 stackblitz 的修复程序 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

很难找到这方面的文档,但可以通过使用 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;

【讨论】:

你救了我。谢谢。 你从哪里得到sort 来自this.dataSource.sort = sort; 我必须把它放在ngAfterViewInit 中才能正常工作 这很好,只是把它放在上面评论中提到的ngAfterViewInit中。 当使用“严格”TypeScript 时,item[property] 会导致错误(假设item 是某个类型化对象)。对于那些情况,我发现这个答案很有用:***.com/a/55108590/53538 这是关于强制类型化对象“可索引”。【参考方案3】:

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

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

    default: return item[property];
    
  ;

【讨论】:

【参考方案4】:

您可以在组件中编写一个函数来从对象中获取深层属性。然后在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>

【讨论】:

这似乎是最好的解决方案,小巧简洁,而且没有switch那么受限。 我真的很喜欢这个实现。减少必须使用/生成的代码。我在之前的 mat 表的最后一个实现中遇到了问题,刷新导致了问题。不过这很干净。 我也喜欢这个解决方案。我在我的项目中使用lodash,所以如果您使用lodash,此解决方案将转换为:this.dataSource.sortingDataAccessor = _.get; 无需重新发明深层属性访问。 @andy 你应该把它作为一个单独的答案。在评论中听起来太简单了。。这就是我所要做的吗?【参考方案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;
  ;

【讨论】:

您的解决方案对我帮助最大,因为我意识到我可以返回数字或字符串。我的表有两种类型,需要对数字进行排序,而不是像字符串那样排序。使用检查输入的三元运算符是解决方案的关键。 我得到了Cannot find name '_isNumbervalue,假设这是一个lodash方法,我在节点模块中找不到该方法。 isNumber存在。如果是这样的话,我以前不熟悉 lodash。这个怎么用? 从“@angular/cdk/coercion”导入_isNumberValue;【参考方案6】:

我使用了一个通用方法,它允许您使用带有mat-sort-headermatColumnDef 的dot.seperated.path。如果找不到路径指定的属性,则此操作将失败以静默方式返回 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;

【讨论】:

1000% 应该是公认的解决方案。这是唯一没有为我抛出 typeErrors 的解决方案。 只是不要忘记提及 matColumnDef 需要匹配显示的列,就像 path.property 一样,两者都匹配“Address.CompanyName”。这个答案救了我。【参考方案7】:

给出的答案甚至可以缩短,无需切换,只要您对字段使用点表示法。

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;

【讨论】:

【参考方案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; 

无需重新发明深层属性访问。

【讨论】:

效果很好,但对于任何挣扎的人:您应该将displayedColumns命名为值的路径,即['title', 'value', 'user.name'];,然后在您的模板中使用&lt;ng-container matColumnDef="user.name"&gt; 或者,您可以保留列名原样并通过 mat-sort-header 独立覆盖 sortHeaderId,例如mat-sort-header="user.name"【参考方案10】:

使用 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;

【讨论】:

【参考方案11】:

只需将其添加到您的数据源,您就可以访问嵌套对象

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

【讨论】:

【参考方案12】:

如果您想要一个具有一些扩展功能的 Angular 材质表,例如对嵌套对象进行排序,请查看 https://github.com/mikelgo/ngx-mat-table-extensions/blob/master/libs/ngx-mat-table/README.md。

我创建了这个库,因为我缺少一些开箱即用的 mat-table 功能。

高级排序类似于@Hieu Nguyen 建议的答案,但稍微扩展为也可以按大小写字母进行正确排序。

【讨论】:

【参考方案13】:

我的表格列排序不正确,因此我修改了其中一个答案以使用我的数据。

function pathDataAccessor(item: any, path: string): any 
  return (item: any, path: string): any => 
    return path.split(".").reduce((accumulator: any, key: string) => 
      let returnValue;
      if (accumulator) 
        returnValue = accumulator[key];
       else 
        returnValue = undefined;
      
      if (typeof returnValue === "string") 
        returnValue = returnValue.trim().toLocaleLowerCase();
      
      return returnValue;
    , item);
  ;


【讨论】:

以上是关于Angular Material 2 DataTable 使用嵌套对象排序的主要内容,如果未能解决你的问题,请参考以下文章

使用 Angular-cli 在 Angular 2 应用程序中安装 bootstrap-material-design

Angular 2.0 Material MdDialog 与 Angular 2.0 的工作示例

Angular 5 Angular Material 2 - 使用 minLength 自动完成

Angular Material 2 Textarea Autosize 属性 (mdTextareaAutosize)

尝试从 npm 安装 Angular 2 Material - 源文本中无法识别的标记

Angular 2 Material - 如何有条件地设置工具栏的颜色