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-header
或matColumnDef
的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'];
,然后在您的模板中使用<ng-container matColumnDef="user.name">
。
或者,您可以保留列名原样并通过 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)