【问题标题】:Angular Material 2 DataTable Sorting with nested objectsAngular Material 2 DataTable 使用嵌套对象排序
【发布时间】: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 的修复程序

标签: angular angular-material2


【解决方案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 是某个类型化对象)。对于那些情况,我发现这个答案很有用:stackoverflow.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);
                  };
                }
                
                

                【讨论】:

                  猜你喜欢
                  • 2020-02-07
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-02-20
                  • 2019-06-25
                  • 2021-02-26
                  • 1970-01-01
                  相关资源
                  最近更新 更多