Angular Material 2 DataTable Sắp xếp với các đối tượng lồng nhau


82

Tôi có một DataTable Angular Material 2 bình thường với các tiêu đề sắp xếp. Tất cả các loại tiêu đề đều hoạt động tốt. Ngoại trừ cái có đối tượng là giá trị. Những thứ này không sắp xếp gì cả.

Ví dụ:

 <!-- 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>

lưu ý element.project.name

Đây là cấu hình displayColumn:

 displayedColumns = ['project.name', 'position', 'name', 'test', 'symbol'];

Thay đổi 'project.name'thành 'project'không hoạt động cũng không"project['name']"

Tôi đang thiếu gì? Điều này thậm chí có thể?

Đây là một Stackblitz: Angular Material2 DataTable sắp xếp các đối tượng

Chỉnh sửa: Cảm ơn tất cả các câu trả lời của bạn. Tôi đã làm cho nó hoạt động với dữ liệu động. Vì vậy, tôi không phải thêm câu lệnh switch cho mọi thuộc tính lồng nhau mới.

Đây là giải pháp của tôi: (Tạo một DataSource mới mở rộng MatTableDataSource là không cần thiết)

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
Bạn có thể vui lòng cập nhật stackblitz với bản sửa lỗi không
Satya Ram

Câu trả lời:


164

Thật khó để tìm thấy tài liệu về điều này, nhưng nó có thể thực hiện được bằng cách sử dụng sortingDataAccessorvà một câu lệnh switch. Ví dụ:

@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
Bạn đã cứu tôi. Cảm ơn bạn.
cá chẽm

bạn sortđến từ đâuthis.dataSource.sort = sort;
Joey Gough

3
Tôi đã phải đặt cái này vào ngAfterViewInitđể nó hoạt động
Mel

Điều này hoạt động tốt, chỉ cần đặt nó trong ngAfterViewInit như đã đề cập trong nhận xét ở trên.
Wael

đặt nó bên cạnh khai báo bảng của tôi và nó hoạt động ngay lập tức. Đã tiết kiệm cho tôi rất nhiều lần gỡ lỗi. cảm ơn!
JamieT

29

Bạn có thể viết một hàm trong thành phần để lấy thuộc tính sâu từ đối tượng. Sau đó sử dụng nó dataSource.sortingDataAccessornhư dưới đây

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

Và trong 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
Đây có vẻ là giải pháp tốt nhất, nhỏ gọn và súc tích và nó không bị giới hạn như công tắc.
Ivar Kallejärv

Tôi thực sự thực sự thích thực hiện này. Cắt giảm mã phải được sử dụng / tạo. Tôi đã gặp sự cố với lần triển khai cuối cùng của bảng chiếu với điều này trước đây, các lần làm mới đã gây ra sự cố. Điều này là sạch sẽ mặc dù.
LP

3
Tôi cũng thích giải pháp này. Tôi sử dụng lodashtrong dự án của mình nên nếu bạn sử dụng lodash, giải pháp này có nghĩa là: this.dataSource.sortingDataAccessor = _.get;Không cần phải phát minh lại quyền truy cập thuộc tính sâu.
Andy

1
@andy bạn nên đặt câu trả lời này thành một câu trả lời riêng. Nghe có vẻ quá đơn giản để trở thành sự thật trong một nhận xét .. Đó là tất cả những gì tôi phải làm?
Simon_Weaver

11

Câu trả lời như đã cho thậm chí có thể được rút ngắn, không cần chuyển đổi, miễn là bạn sử dụng ký hiệu dấu chấm cho các trường.

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

Tôi thích @Hieu_Nguyen giải pháp. Tôi sẽ chỉ thêm rằng nếu bạn sử dụng lodash trong dự án của bạn như tôi làm thì giải pháp chuyển thành điều này:

import * as _ from 'lodash';

this.dataSource.sortingDataAccessor = _.get; 

Không cần phải phát minh lại quyền truy cập thuộc tính sâu.


1
Hoạt động tuyệt vời, nhưng đối với bất kỳ ai đang gặp khó khăn: bạn nên đặt tên displayedColumnslà đường dẫn đến các giá trị, tức là ['title', 'value', 'user.name'];và sau đó sử dụng <ng-container matColumnDef="user.name">trong mẫu của bạn.
Jeffrey Roosendaal

4

Tôi sử dụng một phương pháp chung cho phép bạn sử dụng đường dẫn dot.seperated.path với mat-sort-headerhoặc matColumnDef. Điều này không thành công khi trả về không xác định một cách âm thầm nếu nó không thể tìm thấy thuộc tính được chỉ định bởi đường dẫn.

function pathDataAccessor(item: any, path: string): any {
  return path.split('.')
    .reduce((accumulator: any, key: string) => {
      return accumulator ? accumulator[key] : undefined;
    }, item);
}

Bạn chỉ cần đặt bộ truy cập dữ liệu

this.dataSource.sortingDataAccessor = pathDataAccessor;

1

Tôi đã tùy chỉnh cho nhiều cấp độ đối tượng lồng nhau.

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

Giải pháp của bạn đã giúp tôi nhiều nhất vì tôi nhận ra rằng tôi có thể trả về số hoặc chuỗi. Bảng của tôi có cả hai loại và cần được sắp xếp trong đó các số được sắp xếp theo số chứ không phải như chuỗi. Sử dụng toán tử bậc ba để kiểm tra việc nhập là chìa khóa của giải pháp.
TYMG

Tôi đã nhận Cannot find name '_isNumbervaluevà giả sử đây là một phương thức lodash, tôi không thể tìm thấy phương thức trong mô-đun nút. isNumbertồn tại. Trước đây tôi không quen thuộc với lodash nếu đó là cái gì. Làm cách nào để sử dụng cái này?
Rin và Len

1
nhập {_isNumberValue} từ "@ angle / cdk / coercion";
E.Sarawut

1

Một phương án khác, mà không ai ném ra đây, hãy san bằng cột trước ...

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

this.dataSource = new MatTableDataSource(yourData);

Chỉ là một sự thay thế khác, ưu và nhược điểm cho từng loại!


1

Chỉ cần thêm cái này vào nguồn dữ liệu của bạn và bạn sẽ có thể truy cập vào đối tượng lồng nhau

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
Cảm ơn rât nhiều! điều này làm việc hoàn hảo cho tôi.
Paco Zevallos

0

Nó đang cố gắng sắp xếp theo phần tử ['project.name']. Rõ ràng phần tử không có thuộc tính như vậy.

Sẽ dễ dàng tạo một nguồn dữ liệu tùy chỉnh mở rộng MatTableDatasource và hỗ trợ sắp xếp theo các thuộc tính đối tượng lồng nhau. Xem các ví dụ trong tài liệu material.angular.io về cách sử dụng nguồn tùy chỉnh.


0

Tôi đã gặp vấn đề tương tự, bằng cách thử nghiệm đề xuất đầu tiên, tôi gặp một số lỗi, tôi có thể sửa nó bằng cách thêm "switch (thuộc tính)"

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

    default: return item[property];
    }
  };

0

Sử dụng MatTableDataSource Kiểm tra giải pháp vấn đề MatSort hoàn chỉnh

trong 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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.