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();
  }
}
römisch
quelle
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;
}
Steve Sanders
quelle
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>
Hieu Nguyen
quelle
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;
}
Erik Schaareman
quelle
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.

Andy
quelle
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;
Toby Harris
quelle
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;
  };
E. Sarawut
quelle
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!

Tim Harker
quelle
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];
};
Chan Jing Hong
quelle
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.

Funkizer
quelle
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];
    }
  };
kawthar
quelle
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;
}
Priti jha
quelle