Demo Penyortiran Mat-table tidak Berfungsi


108

Saya mencoba membuat mat-tablepenyortiran berfungsi secara lokal, dan sementara saya bisa mendapatkan data untuk muncul seperti yang diharapkan, mengklik baris tajuk tidak melakukan penyortiran seperti pada contoh online (tidak ada yang terjadi sama sekali). Saya mencoba membuat demo ini bekerja secara lokal: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

Saya telah membuat proyek baru dengan Angular CLI, lalu mengikuti langkah-langkah berikut: https://material.angular.io/guide/getting-started

Ini file lokal saya:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatSort, MatTableModule } from '@angular/material';

import { AppComponent } from './app.component';
import { TableSortingExample } from './table-sorting-example';

@NgModule({
  declarations: [
    AppComponent,
    TableSortingExample,
    MatSort
  ],
  imports: [
    BrowserModule,
    MatTableModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
}

app.component.html

<div style="text-align:center">
  <h1>
    Welcome to {{title}}!
  </h1>
  <table-sorting-example></table-sorting-example>
</div>

table-sorting-example.html

<div class="example-container mat-elevation-z8">
  <mat-table #table [dataSource]="dataSource" matSort>

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- ID Column -->
    <ng-container matColumnDef="userId">
      <mat-header-cell *matHeaderCellDef mat-sort-header> ID </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container matColumnDef="progress">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Progress </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.progress}}% </mat-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="userName">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
    </ng-container>

    <!-- Color Column -->
    <ng-container matColumnDef="color">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Color </mat-header-cell>
      <mat-cell *matCellDef="let row" [style.color]="row.color"> {{row.color}} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</div>


<!-- Copyright 2017 Google Inc. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at http://angular.io/license -->

table-sorting-example.ts

import {Component, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk/collections';
import {MatSort} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';

/**
 * @title Table with sorting
 */
@Component({
  selector: 'table-sorting-example',
  styleUrls: ['table-sorting-example.css'],
  templateUrl: 'table-sorting-example.html',
})
export class TableSortingExample {
  displayedColumns = ['userId', 'userName', 'progress', 'color'];
  exampleDatabase = new ExampleDatabase();
  dataSource: ExampleDataSource | null;

  @ViewChild(MatSort) sort: MatSort;

  ngOnInit() {
    this.dataSource = new ExampleDataSource(this.exampleDatabase, this.sort);
  }
}

/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
  'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
  'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
  'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];

export interface UserData {
  id: string;
  name: string;
  progress: string;
  color: string;
}

/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
  get data(): UserData[] { return this.dataChange.value; }

  constructor() {
    // Fill up the database with 100 users.
    for (let i = 0; i < 100; i++) { this.addUser(); }
  }

  /** Adds a new user to the database. */
  addUser() {
    const copiedData = this.data.slice();
    copiedData.push(this.createNewUser());
    this.dataChange.next(copiedData);
  }

  /** Builds and returns a new User. */
  private createNewUser() {
    const name =
      NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
      NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';

    return {
      id: (this.data.length + 1).toString(),
      name: name,
      progress: Math.round(Math.random() * 100).toString(),
      color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
    };
  }
}

/**
 * Data source to provide what data should be rendered in the table. Note that the data source
 * can retrieve its data in any way. In this case, the data source is provided a reference
 * to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
 * the underlying data. Instead, it only needs to take the data and send the table exactly what
 * should be rendered.
 */
export class ExampleDataSource extends DataSource<any> {
  constructor(private _exampleDatabase: ExampleDatabase, private _sort: MatSort) {
    super();
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<UserData[]> {
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._sort.sortChange,
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      return this.getSortedData();
    });
  }

  disconnect() {}

  /** Returns a sorted copy of the database data. */
  getSortedData(): UserData[] {
    const data = this._exampleDatabase.data.slice();
    if (!this._sort.active || this._sort.direction == '') { return data; }

    return data.sort((a, b) => {
      let propertyA: number|string = '';
      let propertyB: number|string = '';

      switch (this._sort.active) {
        case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
        case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
        case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
        case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
      }

      let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
      let valueB = isNaN(+propertyB) ? propertyB : +propertyB;

      return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
    });
  }
}


/**  Copyright 2017 Google Inc. All Rights Reserved.
 Use of this source code is governed by an MIT-style license that
 can be found in the LICENSE file at http://angular.io/license */

Adakah yang tahu mengapa itu akan muncul seperti tabel online tetapi tidak memiliki fungsi penyortiran?


Saya akan men-debug aplikasi terlebih dahulu. Ada kesalahan? jalankan ng test --sm=falsedan lihat apa yang keluar.
k. Vincent

Ini berfungsi untuk saya tanpa @ViewChild (MatSort) sort: MatSort; Alasan apapun ?
pengguna123456

Jawaban:


201

Untuk siapa pun yang mungkin memiliki masalah ini: Masalahnya adalah saya tidak membaca referensi API dengan benar di situs web bahan sudut, bagian yang mengatakan saya harus mengimpor MatSortModule. Setelah saya mengubah daftar impor saya di app.module.ts menjadi

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

itu bekerja dengan baik


45
modul ini tidak disebutkan dalam dokumentasi. material.angular.io/components/table/overview#sorting saya menyia-nyiakan satu jam untuk ini juga.
Sonic Soul

8
tidak apa-apa, di teks header dapat diklik dan ikon juga ada tetapi penyortiran masih tidak berfungsi.
SPnL

3
Periksa apakah BrowserAnimationsModulejuga diimpor di app.module.ts
Augustas

2
Bisakah saya mengatakan mereka SOB? Saya menghabiskan 1 jam mencoba mencari tahu mengapa ViewChild saya tidak berfungsi. Tidak bisakah mereka mengimpor / mengekspor MatSortModule ini dari MatTableModule ??
Sampgun

7
Saya telah mengimpor MatSortModuledan BrowserAnimationsModule, dan saya telah memastikan bahwa nilai matColumnDef cocok dengan nama properti, namun saya masih tidak dapat membuatnya melakukan apa pun.
Trevor

136

Saya mengalami masalah karena fungsi pengurutan berfungsi tetapi tidak dapat menyortir dengan benar. Saya menyadari bahwa matColumnDefharus memiliki nama yang sama dari properti saya class / interfaceyang saya rujuk matCellDef.

Menurut dokumentasi Angular Material :

Secara default, MatTableDataSource mengurutkan dengan asumsi bahwa nama kolom yang diurutkan cocok dengan nama properti data yang ditampilkan kolom.

Sebagai contoh:

<ng-container matColumnDef="name"> 
    <mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
    <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
</ng-container>

The namedalam matColumnDefdirektif harus sama dengan nameyang digunakan dalam <mat-cell>komponen.


1
Apa dalam contoh Anda yang Anda rujuk? Akan sangat membantu untuk melihat antarmuka Anda juga, untuk perbandingan.
Isherwood

1
Saya menggunakan "Id" sebagai nama kolom sedangkan entitas memiliki "id". Perbedaan case membuatnya tidak berjalan (karena miss refactoring) .. Sekarang sudah terpecahkan. Terima kasih
NitinSingh

2
Terima kasih, ini sangat berguna.
Bohao LI

2
@NitinSingh, bagaimana jika Anda perlu memanggil fungsi pada element, seperti ini `{{row.getName ()}}`
codentary

2
Saya benar-benar berutang budi kepada Anda karena saya telah terjebak pada masalah ini untuk sementara waktu dan komentar ini memperbaiki masalah saya.
noel

101

Jika tabel berada di dalam * ngIf, itu tidak akan berfungsi. Ini akan berfungsi jika diubah menjadi [tersembunyi]


34
!!! ANDA SELAMAT HARI SAYA !!! Menggunakan bukan <div *ngIf="xxx"> yang<div [hidden]="!xxx">
Mark

1
Dapat mengonfirmasi, ini juga berhasil untuk saya. Terima kasih zerg!
clo5ure

1
Terima kasih banyak, ini menghabiskan banyak waktu !!
themightylc

1
Atau cukup setel sumber data di ngAfterViewInit alih-alih ngOnInit
user3666653

1
Ini adalah masalah paling "tersembunyi" yang mungkin terjadi, terima kasih atas solusinya! dokumentasi bisa memperingatkan tentang ini
Raycherr

36

nama matColumnDef dan * nama nilai aktual matCellDef harus sama

Contoh:

<ng-container matColumnDef="oppNo">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Opportunity Number</th>
    <td mat-cell *matCellDef="let element">{{element.oppNo}}</td>
</ng-container>

Dalam kasus saya oppNo sama untuk nama matColumnDef dan * nama matCellDef dan penyortiran berfungsi dengan baik.


Menarik. Itu juga yang terjadi pada saya. Tapi, apakah Anda tahu alasan sebenarnya di balik ini atau itu sebenarnya semacam "bug"?
ReturnTable

22

Menambahkan urutan dalam blok batas waktu berfungsi untuk saya,

dataSource = new MatTableDataSource(this.articleService.getAllArticles());
setTimeout(() => {
  this.tableDataSource.sort = this.sort;
  this.tableDataSource.paginator = this.paginator;
});

Jika Anda tidak ingin menggunakan kait penyelamat.


1
peretasan bodoh tetapi berhasil, tahu mengapa tidak berhasil tanpa batas waktu?
Ruben

Saya menghabiskan waktu terlalu lama untuk mencoba segala hal lainnya, berpikir saya akan menjadi gila. Bekerja seperti pesona!
willpnw

4
Benar-benar cara yang buruk untuk melakukannya. Ini berfungsi karena Anda membiarkan beberapa waktu berlalu setelah inisialisasi komponen sehingga dataSource dibuat, lalu Anda menambahkan sortir dan paginator. Cara terbaik adalah dengan memindahkan buidling datSource di ngOnInit, lalu pindahkan tugas sortir dan paginator di AfterViewInit. Itulah tujuan dari Lifecycle hooks.
Selam Getachew

21

Saya juga mengalami masalah ini. Karena Anda perlu menunggu turunannya ditentukan, Anda harus mengimplementasikan dan menggunakan AfterViewInit, bukan onInit.

  ngAfterViewInit (){
    this.dataSource.sort = this.sort;
  }

Hebat! Terima kasih
Shashank Vivek

Saya menggunakan tabel dengan pengurutan, pemfilteran, dan penomoran halaman. Apakah Anda tahu mengapa hanya penyortiran yang harus ditentukan ngAfterViewInit? Sisanya bekerja dari ngOnInit. Ini hanya untuk mencoba memahami, itu diperbaiki berkat Anda
Nicolas M.

14

Saya menghabiskan berjam-jam untuk masalah ini. Setelah membaca sejumlah utas, berikut adalah langkah-langkah yang saya lakukan.

  1. Seperti yang disebutkan @avern , Anda perlu mengimporMatSortModule .
  2. Pastikan Anda TIDAK menyertakan tabel dalam file *ngIf. Ubah menjadi [hidden]seperti yang direkomendasikan @zerg . (Saya tidak mengerti mengapa)

Semoga ini membantu.


Ini telah menyia-nyiakan hari saya untuk mencari tahu masalah dan bodoh tidak menunjukkan kesalahan apa pun.
penampungan surekha

11

Solusi saya adalah memperbaiki beberapa hal (pada dasarnya menggabungkan sebagian besar solusi di halaman ini).

Hal-hal yang perlu diperiksa:

  1. BrowserModule, MatTableModule, MatSortModule Modul harus diimpor di file modul root.
  2. Pastikan sudah pernah digunakan MatTableDatasource kelas dan meneruskan larik data Anda di dalamnya sebagai parameter
  3. Pastikan tabel Anda tidak bersarang di sebuah *ngIf=....direktif. Gunakan operasi bersyarat lain sebagai gantinya (masih belum mengerti mengapa).

3

Bagi saya, mengganti * ngIf dengan atribut [tersembunyi] untuk tag mat-table berfungsi. Bagaimana cara memposting ini sebagai bug ke komunitas Angular Material?


3

Saya memperbaiki ini dalam skenario saya dengan menamai data tabel dengan nama yang sama dengan * matColumnDef Misalnya:

<!-- Name Column -->
<ng-container matColumnDef="name">
  <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
  <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
</ng-container>

Sebagai gantinya

<!-- Name Column -->
    <ng-container matColumnDef="userName">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
    </ng-container>

3

Ada 2 masalah bagi saya.

  1. Nama matColumnDef dan matCellDef -> berbeda
  2. Saya mendapatkan data dari layanan. Jenis ngOnInit tidak berfungsi. Digantikan dengan

    ngAfterViewInit () {this.dataSource.sort = this.sort; }


2

Saya menemukan blog lama ini yang membantu saya membuatnya berfungsi: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Pastikan untuk mengimpor MatSortModule
  2. Tentukan matSort tajuk
  3. Pastikan untuk menggabungkan sumber data Anda dalam file MatTableDataSource
    • Ini adalah salah satu yang membantu saya mengatasinya (mendapatkannya? Semacam itu). Dalam template saya merujuk ke array secara langsung ( <table mat-table [dataSource]="this.products" matSort>) tetapi saya seharusnya menggunakan objek sumber data yang saya inisialisasi di code ( <table mat-table [dataSource]="this.dataSource" matSort>). Sumber data diinisialisasi sepertidataSource = new MatTableDataSource(this.products)
  4. Beri tahu sumber data tentang penyortiran Anda, di ngOnInit/ngAfterViewInit
  5. Tuliskan jenis Anda sendiri, jika Anda tidak ingin menggunakan MatTableDataSource

1

Jika tabel Anda berada di dalam * ngIf dan menurut Anda ada hubungannya dengan tidak mengurutkan tabel Anda, maka menentukan sortingDataAccessorfungsi Anda sendiri mungkin menyelesaikan masalah seperti yang terjadi pada saya. Saya memiliki tabel saya di dalam beberapa * ngIfs dan mengeluarkannya dari * ngIfs itu tidak masuk akal:

`ngAfterViewInit(): void {
        this.matchesDataSource.sort = this.sort;
        this.matchesDataSource.sortingDataAccessor = previewMatchSortingFn;
    }`

`export function previewMatchSortingFn(item: Match, header: string): string | number {
    switch (header) {
        case 'home':
            return item.homeTeam.name;
        case 'away':
            return item.awayTeam.name;
        case 'date':
            if (item.dateTime) {
                // this will return the number representation of the date
                return item.dateTime.valueOf();
            }
            return;
        default:
            break;
    }
}`

1

Salah satu alasan MatSort mungkin tidak berfungsi, adalah ketika itu ditambahkan ke dataSource (yaitu this.dataSource.sort = this.sort) sebelum didefinisikan. Ada beberapa alasan untuk ini:

  1. jika Anda menambahkan jenis di ngOnInit. Pada titik ini template belum dirender, jadi MatSort yang Anda dapatkan @ViewChild(MatSort, { static: true }) sort: MatSort;belum ditentukan dan dapat dimengerti tidak akan melakukan apa pun. Solusi untuk masalah ini adalah berpindah this.dataSource.sort = sortke ngAfterViewInit. Saat ngAfterViewInit dipanggil, komponen Anda dirender, dan MatSort harus ditentukan.

  2. ketika Anda menggunakan * ngIf adalah template Anda pada elemen tabel Anda atau satu jika itu elemen induk dan ini * ngIf menyebabkan tabel Anda tidak dirender pada saat Anda mencoba menyetel MatSort. Misalnya, jika Anda memiliki *ngIf="dataSource.data.length > 0"elemen tabel Anda (untuk hanya merendernya jika ada data yang ada) dan Anda menetapkan this.dataSource.sort = this.sorttepat setelah Anda menyetel this.dataSource.datadengan data Anda. Tampilan komponen belum dirender, jadi MatSort masih belum ditentukan.

Agar MatSort berfungsi dan tetap menampilkan tabel secara bersyarat, Anda dapat memutuskan untuk menggantinya *ngIfdengan[hidden] seperti yang dinyatakan dalam beberapa jawaban lainnya. Namun, jika Anda ingin menyimpan pernyataan * ngIf, Anda dapat menggunakan solusi berikut. Solusi ini berfungsi untuk Angular 9, saya belum mengujinya pada versi sebelumnya jadi saya tidak yakin apakah itu berfungsi di sana.

Saya menemukan solusi ini di sini: https://github.com/angular/components/issues/10205

Alih-alih menempatkan:

@ViewChild(MatSort) sort: MatSort;

gunakan penyetel untuk matSort. Penyetel ini akan aktif setelah matSort dalam tampilan Anda berubah (yaitu ditentukan pertama kali), penyetel ini tidak akan aktif saat Anda mengubah penyortiran dengan mengklik panah. Ini akan terlihat seperti ini:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    this.dataSource.sort = sort;
}

Jika Anda memiliki fungsi lain yang (secara terprogram) mengubah pengurutan, saya tidak yakin apakah akan diaktifkan lagi, saya belum mengujinya. Jika Anda tidak ingin memastikan itu hanya menyetel pengurutan jika pengurutannya tidak ditentukan, Anda dapat melakukan sesuatu seperti ini:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    if (!this.dataSource.sort) {
        this.dataSource.sort = sort;
    }
}

0

Sebenarnya nama matColumnDef (yaitu nama kolom) dan nama properti Kelas / Antarmuka Anda harus sama agar dapat berfungsi.

Terkadang kami tidak dapat mengubah nama properti Kelas / Antarmuka kami, dalam hal ini, kami dapat menerapkan pengurutan khusus seperti di bawah ini.

let say your columns  as  ['id', 'name'] and 
your class/interface  as  ['userId', 'name']

jika kita melakukan penyortiran pada kolom 'id' itu tidak akan berhasil. Coba dengan penyortiran khusus

this.dataSource.sortingDataAccessor = (item,property)=>{

 // where item is your class/interface data
 // where property is your column name

       switch(property){
           case 'id' : return item.userId
           default: return item[property];
        }
}

0

Bagi siapa pun yang bingung tentang penamaan ini harus sama, saya melakukan beberapa pengujian:

Ini akan berfungsi (nama properti sama dengan kolom def):

<ng-container matColumnDef="version">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version']

Ini TIDAK akan berfungsi (nama properti tidak sama dengan kolom def):

<ng-container matColumnDef="version2">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version2']

Fyi, ini juga TIDAK berfungsi (panjang properti):

<ng-container matColumnDef="length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['length']

Dan ini juga tidak:

<ng-container matColumnDef="ids.length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['ids.length']

-1

Lihat apakah Anda memiliki kesalahan javascript di konsol. Bisa jadi ada hal lain yang gagal sebelum penyortiran Anda diinisialisasi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.