Angular Material 2DataTableネストされたオブジェクトを使用した並べ替え


83

ソートヘッダー付きの通常のAngularMaterial 2DataTableがあります。すべてのソートはヘッダーが正常に機能します。値としてオブジェクトを持つものを除いて。これらはまったくソートされません。

例えば:

 <!-- 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は次のとおりです。AngularMaterial2DataTable ソートオブジェクト

編集: すべての回答をありがとう。私はすでにそれを動的データで動作させています。したがって、ネストされた新しいプロパティごとにswitchステートメントを追加する必要はありません。

これが私の解決策です:(MatTableDataSourceを拡張する新しいデータソースを作成する必要はありません)

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更新してくださいでした
サティヤラム

回答:


168

これに関するドキュメントを見つけるのは困難でしたが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
あなたは私を救いました。ありがとうございました。
シーバス

1
どこsortから来ましたかthis.dataSource.sort = sort;
Joey Gough 2018年

3
私はngAfterViewInitそれが機能するためにこれを配置しなければなりませんでした
Mel

これはうまく機能し、上記のコメントで述べたようにngAfterViewInitに配置するだけです。
ワエル・

これを私のテーブル宣言の隣に置くと、すぐに機能しました。デバッグを大幅に節約できました。ありがとう!
JamieT

31

コンポーネントに関数を記述して、オブジェクトから深くプロパティを取得できます。次に、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
これは、小さくて簡潔な最善の解決策のようであり、スイッチほど制限されていません。
アイヴァーKallejärv

私はこの実装が本当に好きです。使用/生成する必要のあるコードを削減します。以前、これを使用したマットテーブルの最後の実装で問題が発生しました。更新により、問題が発生していました。これはきれいです。
LP

3
私もこのソリューションが好きです。私lodashは自分のプロジェクトで使用しているので、を使用する場合lodash、このソリューションは次のように解釈されthis.dataSource.sortingDataAccessor = _.get;ます。深いプロパティアクセスを再発明する必要はありません。
アンディ

2
@andyこれは別の答えにする必要があります。コメントで真実を語るには単純すぎるように聞こえます。私がしなければならないのはそれだけですか?
Simon_Weaver

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は、値へのパスとして 'sに名前を付けてから、テンプレートで['title', 'value', 'user.name'];使用する必要<ng-container matColumnDef="user.name">があります。
JeffreyRoosendaal20年

または、列名をそのままにして、mat-sort-headerたとえばmat-sort-header="user.name"
p4m

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に精通していません。これはどのように使用しますか?
リンとレン

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

0

element ['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;
}

0

ネストされたオブジェクトの並べ替えなど、いくつかの拡張機能を備えたAngularマテリアルテーブルが必要な場合は、https://github.com/mikelgo/ngx-mat-table-extensions/blob/master/libs/ngx-matを参照してください。 -table /README.md

箱から出してマットテーブルのいくつかの機能が欠けていたので、このライブラリを作成しました。

高度な並べ替えは、@ Hieu Nguyenが提案した回答に似ていますが、大文字と小文字による適切な並べ替えもできるように少し拡張されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.