Açısal Malzeme 2 İç içe nesnelerle DataTable Sıralama


83

Sıralama başlıklarına sahip normal bir Angular Material 2 DataTable'ım var. Her tür başlıklar iyi çalışıyor. Değer olarak bir nesneye sahip olanlar hariç. Bunlar hiç sıralanmıyor.

Örneğin:

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

not et element.project.name

DisplayColumn yapılandırması şöyledir:

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

Değişen 'project.name'için 'project'çalışmıyor ne de"project['name']"

Neyi kaçırıyorum? Bu mümkün mü?

İşte bir Stackblitz: Angular Material2 DataTable sıralama nesneleri

Düzenleme: Tüm yanıtlarınız için teşekkürler. Zaten dinamik verilerle çalıştırıyorum. Bu nedenle, her yeni iç içe geçmiş mülk için bir anahtar ifadesi eklemem gerekmiyor.

İşte benim çözümüm: (MatTableDataSource'u genişleten yeni bir DataSource oluşturmak gerekli değildir)

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
Lütfen stackblitz'i düzeltmeyle günceller misiniz
Satya Ram

Yanıtlar:


168

Bununla ilgili belge bulmak zordu, ancak sortingDataAccessorve bir switch deyimi kullanarak mümkündür . Örneğin:

@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
beni kurtardın. Teşekkür ederim.
levrek

1
nerden aldın sortgelenthis.dataSource.sort = sort;
Joey Gough

3
ngAfterViewInitÇalışması için bunu yerleştirmek zorunda kaldım
Mel

Bu iyi çalıştı, sadece yukarıdaki yorumda belirtildiği gibi ngAfterViewInit'e yerleştirildi.
Wael

bunu tablo beyanımın yanına yerleştirdi ve anında çalıştı. Beni bir sürü hata ayıklamadan kurtardı. Teşekkürler!
JamieT

31

Nesneden derin bir özellik elde etmek için bileşene bir işlev yazabilirsiniz. O zaman dataSource.sortingDataAccessoraşağıdaki gibi kullanın

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

Ve html olarak

<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
Bu, küçük ve özlü en iyi çözüm gibi görünüyor ve anahtar kadar sınırlı değil.
Ivar Kallejärv

Bu uygulamayı gerçekten çok seviyorum. Kullanılması / oluşturulması gereken kodu azaltır. Daha önce bununla mat tabloların son uygulamasıyla ilgili bir sorunla karşılaştım, yenilemelere neden oluyordu. Yine de bu temiz.
LP

3
Ben de bu çözümleri seviyorum. Projemde kullanıyorum , bu lodashyüzden kullanırsanız lodash, bu çözüm şu this.dataSource.sortingDataAccessor = _.get;anlama gelir: Derin mülk erişimini yeniden icat etmeye gerek yok.
Andy

2
@andy bunu ayrı bir cevap yapmalısınız. bir yorumda gerçek olamayacak kadar basit geliyor .. Tüm yapmam gereken bu mu?
Simon_Weaver

11

Alanlar için nokta notasyonunu kullandığınız sürece, verilen cevap kısaltılabilir, herhangi bir anahtar gerekmez.

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 çözümlerini seviyorum. Sadece ekleyeceğim, projenizde benim yaptığım gibi lodash kullanırsanız, çözüm şuna dönüşür:

import * as _ from 'lodash';

this.dataSource.sortingDataAccessor = _.get; 

Derin mülk erişimini yeniden keşfetmeye gerek yok.


1
Harika çalışıyor, ancak mücadele eden herkes için: displayedColumns'ları değerlere giden yol olarak adlandırmalısınız, yani ['title', 'value', 'user.name'];ve sonra <ng-container matColumnDef="user.name">şablonunuzda kullanmalısınız .
Jeffrey Roosendaal

Alternatif olarak, sütun adlarını olduğu gibi bırakabilir ve sortHeaderId'yi bağımsız olarak geçersiz kılabilirsiniz: mat-sort-headerörn.mat-sort-header="user.name"
p4m

4

Sana bir dot.seperated.path kullanmaya olanak veren genel yöntemi kullanmak mat-sort-headerveya matColumnDef. Bu, yol tarafından dikte edilen özelliği bulamazsa, sessizce tanımsız döndürerek başarısız olur.

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

Sadece veri erişimcisini ayarlamanız gerekiyor

this.dataSource.sortingDataAccessor = pathDataAccessor;

1

Birden çok iç içe nesne seviyesi için özelleştirdim.

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

Numarayı veya dizeyi döndürebileceğimi fark ettiğim için çözümünüz bana en çok yardımcı oldu. Masamın her iki türü de vardır ve sayıların sayısal olarak sıralandığı ve dizelere benzemediği yerlerde sıralanması gerekir. Yazmayı kontrol eden üçlü operatörün kullanılması çözümün anahtarı oldu.
TYMG

Ben var Cannot find name '_isNumbervalueve bu lodash yöntemidir varsayarak, ben düğüm modülünde yöntemini bulamıyor. isNumbervar. Daha önce Lodash'a aşina değilim, eğer buysa. Bunu nasıl kullanırım?
Rin ve Len,

1
{_isNumberValue} değerini "@ angular / cdk / coercion" dan içe aktar;
E.Sarawut

1

Burada kimsenin atmadığı başka bir alternatif, önce sütunu düzleştirin ...

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

this.dataSource = new MatTableDataSource(yourData);

Her biri için başka bir alternatif, artıları ve eksileri!


1

Bunu veri kaynağınıza ekleyin ve yuvalanmış nesneye erişebileceksiniz.

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

['Proje.adı'] öğesine göre sıralamaya çalışıyor. Açıkçası elementin böyle bir özelliği yok.

MatTableDatasource'u genişleten ve yuvalanmış nesne özelliklerine göre sıralamayı destekleyen özel bir veri kaynağı oluşturmak kolay olmalıdır. Özel bir kaynak kullanmak için material.angular.io docs içindeki örnekleri inceleyin.


0

Aynı sorunu yaşadım, ilk öneriyi test ederek bazı hatalar yaşadım, "anahtar (özellik)" ekleyerek düzeltebilirdim

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

    default: return item[property];
    }
  };

0

MatTableDataSource Check eksiksiz MatSort sorun çözümünü kullanın

HTML'de

    <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

İç içe nesneler için sıralama gibi bazı genişletilmiş özelliklere sahip bir Açısal malzeme tablosuna sahip olmak istiyorsanız, https://github.com/mikelgo/ngx-mat-table-extensions/blob/master/libs/ngx-mat adresine bakın. -tablo / README.md .

Bu kitaplığı yarattım çünkü mat tablonun bazı özelliklerini kutudan çıkarıyordum.

Gelişmiş sıralama, @Hieu Nguyen'in önerdiği cevaba benzer, ancak büyük ve küçük harflere göre uygun sıralamaya sahip olmak için biraz genişletildi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.