Angular Material 2 DataTable Sortieren mit verschachtelten Objekten


82

Ich habe eine normale Angular Material 2 DataTable mit Sortierüberschriften. Alle Sortierungen sind Header funktionieren gut. Außer dem mit einem Objekt als Wert. Diese sortieren überhaupt nicht.

Zum Beispiel:

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

beachten Sie das element.project.name

Hier ist die displayColumn-Konfiguration:

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

Das Wechseln 'project.name'zu 'project'funktioniert weder noch"project['name']"

Was vermisse ich? Ist das überhaupt möglich?

Hier ist ein Stackblitz: Angular Material2 DataTable- Sortierobjekte

Edit: Danke für all deine Antworten. Ich habe es bereits mit dynamischen Daten arbeiten lassen. Ich muss also nicht für jede neue verschachtelte Eigenschaft eine switch-Anweisung hinzufügen.

Hier ist meine Lösung: (Das Erstellen einer neuen DataSource, die MatTableDataSource erweitert, ist nicht erforderlich.)

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
Könnten Sie bitte den Stackblitz mit dem Fix aktualisieren
Satya Ram

Antworten:


164

Es war schwierig, eine Dokumentation dazu zu finden, aber es ist möglich, sortingDataAccessoreine switch-Anweisung zu verwenden. Zum Beispiel:

@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
Du hast mich gerettet. Vielen Dank.
Seebarsch

wo hast du sortvon inthis.dataSource.sort = sort;
Joey Gough

3
Ich musste dies ngAfterViewInit
Mel

Dies funktionierte gut, platzierte es einfach in ngAfterViewInit, wie im obigen Kommentar erwähnt.
Wael

platzierte dies neben meiner Tabellendeklaration und es funktionierte sofort. Ersparte mir eine Menge Debugging. Vielen Dank!
JamieT

29

Sie können eine Funktion in eine Komponente schreiben, um tiefgreifende Eigenschaften vom Objekt zu erhalten. Verwenden Sie es dann dataSource.sortingDataAccessorwie unten beschrieben

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

Und in 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
Dies scheint die beste Lösung zu sein, klein und prägnant, und sie ist nicht so eingeschränkt wie der Schalter.
Ivar Kallejärv

Diese Implementierung gefällt mir wirklich sehr gut. Reduziert den Code, der verwendet / generiert werden muss. Ich hatte zuvor ein Problem mit der letzten Implementierung der Mat-Tabellen. Aktualisierungen verursachten Probleme. Das ist aber sauber.
LP

3
Ich mag diese Lösungen auch. Ich verwende es lodashin meinem Projekt. Wenn Sie es verwenden lodash, bedeutet diese Lösung Folgendes: Sie this.dataSource.sortingDataAccessor = _.get;müssen den Deep Property-Zugriff nicht neu erfinden.
Andy

1
@andy du solltest dies eine separate Antwort machen. Es klingt zu einfach, um in einem Kommentar wahr zu sein. Ist das alles, was ich tun muss?
Simon_Weaver

11

Die angegebene Antwort kann sogar gekürzt werden, ohne dass ein Schalter erforderlich ist, solange Sie die Punktnotation für die Felder verwenden.

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

Ich mag @Hieu_Nguyen Lösungen. Ich füge nur hinzu, dass, wenn Sie lodash in Ihrem Projekt wie ich verwenden, die Lösung folgendermaßen übersetzt wird:

import * as _ from 'lodash';

this.dataSource.sortingDataAccessor = _.get; 

Sie müssen den Deep Property Access nicht neu erfinden.


1
Funktioniert wunderbar, aber für alle, die Probleme haben: Sie sollten displayedColumns's als Pfad zu den Werten benennen , dh ['title', 'value', 'user.name'];und dann <ng-container matColumnDef="user.name">in Ihrer Vorlage verwenden.
Jeffrey Roosendaal

4

Ich verwende eine generische Methode, mit der Sie einen dot.seperated.path mit mat-sort-headeroder verwenden können matColumnDef. Dies schlägt unbemerkt fehl, wenn die vom Pfad vorgegebene Eigenschaft nicht gefunden werden kann.

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

Sie müssen nur den Datenzugriff festlegen

this.dataSource.sortingDataAccessor = pathDataAccessor;

1

Ich habe für mehrere verschachtelte Objektebene angepasst.

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

Ihre Lösung hat mir am meisten geholfen, als mir klar wurde, dass ich eine Nummer oder eine Zeichenfolge zurückgeben kann. Meine Tabelle hat beide Typen und musste sortiert werden, wobei die Zahlen numerisch und nicht wie Zeichenfolgen sortiert wurden. Die Verwendung des ternären Operators, der die Eingabe überprüft, war der Schlüssel zur Lösung.
TYMG

Ich habe Cannot find name '_isNumbervalueund vorausgesetzt, dies ist eine lodash-Methode, kann ich die Methode im Knotenmodul nicht finden. isNumberexistiert. Ich bin bisher nicht mit lodash vertraut, wenn es das ist. Wie benutze ich das?
Rin und Len

1
{_isNumberValue} aus "@ angle / cdk / coercion" importieren;
E. Sarawut

1

Eine andere Alternative, die hier niemand rausgeworfen hat, ist, die Säule zuerst zu plattieren ...

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

this.dataSource = new MatTableDataSource(yourData);

Nur eine weitere Alternative, Vor- und Nachteile für jeden!


1

Fügen Sie dies einfach Ihrer Datenquelle hinzu und Sie können auf das verschachtelte Objekt zugreifen

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
Vielen Dank! Das hat perfekt für mich funktioniert.
Paco Zevallos

0

Es wird versucht, nach Element ['project.name'] zu sortieren. Offensichtlich hat element keine solche Eigenschaft.

Es sollte einfach sein, eine benutzerdefinierte Datenquelle zu erstellen, die MatTableDatasource erweitert und das Sortieren nach verschachtelten Objekteigenschaften unterstützt. Schauen Sie sich die Beispiele in den Dokumenten zu material.angular.io zur Verwendung einer benutzerdefinierten Quelle an.


0

Ich hatte das gleiche Problem, indem ich den ersten Satz testete, bei dem ich einige Fehler hatte, konnte ich ihn beheben, indem ich "switch (property)" hinzufügte.

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

    default: return item[property];
    }
  };

0

Verwenden MatTableDataSource prüfen komplette MatSort Problem Lösung

in 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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.