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를 확장하는 새 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();
  }
}

1
Stackblitz를 수정하여 업데이트 해 주시겠습니까
Satya Ram

답변:


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
당신이 나를 구해주었습니다. 감사합니다.
seabass

어디 거지 sort의에서this.dataSource.sort = sort;
조이 고흐

3
나는이를 배치했다 ngAfterViewInit작동하기

이것은 잘 작동했으며 위의 주석에서 언급했듯이 ngAfterViewInit에 배치했습니다.
Wael

이것을 내 테이블 선언 옆에 배치하면 즉시 작동했습니다. 많은 디버깅을 할 수있었습니다. 감사!
JamieT 2019

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
이것은 작고 간결한 최상의 솔루션으로 보이며 스위치만큼 제한되지 않습니다.
Ivar Kallejärv

이 구현이 정말 마음에 듭니다. 사용 / 생성해야하는 코드를 줄입니다. 이전에 매트 테이블의 마지막 구현에 문제가 발생했으며 새로 고침으로 인해 문제가 발생했습니다. 그래도 이것은 깨끗합니다.
LP

3
이 솔루션도 마음에 듭니다. lodash내 프로젝트에서 사용 하므로을 사용 lodash하면이 솔루션은 다음과 같이 해석됩니다 this.dataSource.sortingDataAccessor = _.get;. 깊은 속성 액세스를 재발 명 할 필요가 없습니다.
Andy

1
@andy 당신은 이것을 별도의 대답으로 만들어야합니다. 댓글에 사실이라고 말하기에는 너무 간단하게 들립니다. 그게 내가해야 할 전부입니까?
Simon_Weaver 19

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 메서드라고 가정하면 노드 모듈에서 메서드를 찾을 수 없습니다. 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

요소 [ 'project.name']별로 정렬하려고합니다. 분명히 요소에는 그러한 속성이 없습니다.

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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.