Mat-Table Sorting Demo funktioniert nicht


104

Ich versuche, die mat-tableSortierung lokal zum Laufen zu bringen, und obwohl die Daten wie erwartet angezeigt werden können, führt das Klicken auf die Kopfzeile die Sortierung nicht wie bei Online-Beispielen durch (es passiert überhaupt nichts). Ich versuche, diese Demo lokal zum Laufen zu bringen: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

Ich habe ein neues Projekt mit Angular CLI erstellt und dann die folgenden Schritte ausgeführt: https://material.angular.io/guide/getting-started

Hier sind meine lokalen Dateien:

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 */

Hat jemand eine Idee, warum es wie die Online-Tabelle angezeigt wird, aber die Sortierfunktion fehlt?


Ich würde die App zuerst debuggen. Irgendwelche Fehler? Laufen Sie die ng test --sm=falseund sehen Sie, was herauskommt.
k.vincent

Es funktioniert bei mir ohne @ViewChild (MatSort) sort: MatSort; Irgendein Grund?
user123456

Antworten:


196

Für alle anderen, die dieses Problem haben könnten: Das Problem war, dass ich die API-Referenz auf der Website für eckige Materialien nicht richtig gelesen habe. Dieser Teil besagte, dass ich MatSortModule importieren musste. Nachdem ich meine Importliste in app.module.ts in geändert habe

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

es hat gut funktioniert


44
Dieses Modul wird in der Dokumentation nicht erwähnt. material.angular.io/components/table/overview#sorting Ich habe auch eine Stunde damit verschwendet.
Sonic Soul

8
Dies ist in Ordnung, in der Kopfzeile ist der Text anklickbar und das Symbol ist ebenfalls vorhanden, aber die Sortierung funktioniert immer noch nicht.
SPnL

2
Überprüfen Sie, ob BrowserAnimationsModuleauch in app.module.ts
Augustas

2
Kann ich sagen, dass sie SOBs sind? Ich habe 1 Stunde lang versucht herauszufinden, warum mein ViewChild nicht funktioniert hat. Können sie dieses MatSortModule nicht aus dem MatTableModule importieren / exportieren?
Sampgun

6
Ich habe das MatSortModuleund importiert und BrowserAnimationsModulesichergestellt, dass der matColumnDef-Wert mit dem Eigenschaftsnamen übereinstimmt, aber ich kann ihn immer noch nicht dazu bringen, etwas zu tun.
Trevor

130

Ich hatte ein Problem damit, dass die Sortierfunktion funktionierte, aber nicht richtig sortierte. Mir wurde klar, dass matColumnDefderselbe Name der Eigenschaft von mir sein muss class / interface, auf die ich mich beziehe matCellDef.

Gemäß der Angular Material- Dokumentation :

Standardmäßig sortiert die MatTableDataSource unter der Annahme, dass der Name der sortierten Spalte mit dem Namen der Dateneigenschaft übereinstimmt, die in der Spalte angezeigt wird.

Zum Beispiel:

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

Die namein der matColumnDefDirektive muss mit der namein der <mat-cell>Komponente verwendeten identisch sein .


1
Worauf beziehen Sie sich in Ihrem Beispiel? Zum Vergleich wäre es hilfreich, auch Ihre Benutzeroberfläche zu sehen.
Isherwood

1
Ich habe "Id" als Spaltennamen verwendet, während die Entität "id" hatte. Der Fallunterschied bestand darin, dass es nicht ausgeführt wurde (aufgrund eines Refactoring-Fehlers). Jetzt ist es gelöst. Vielen Dank
NitinSingh

2
Danke, es ist sehr nützlich.
Bohao LI

2
@NitinSingh, was ist, wenn Sie eine Funktion auf dem aufrufen müssen element, wie diese `{{row.getName ()}}`
codentary

1
Ich schulde dir ein Bier, weil ich eine Weile an diesem Problem festgehalten habe und dieser Kommentar mein Problem behoben hat.
Noel

98

Wenn sich die Tabelle in * ngIf befindet, funktioniert sie nicht. Es wird funktionieren, wenn es in [versteckt] geändert wird.


33
!!! SIE SPAREN MEINEN TAG !!! Verwenden Sie anstelle <div *ngIf="xxx"> der<div [hidden]="!xxx">
Mark

1
Kann bestätigen, das hat auch bei mir funktioniert. Danke zerg!
Clo5ure

1
Vielen Dank, das hat mich so viel Zeit gekostet !!
Themightylc

1
Oder setzen Sie einfach die Datenquelle in ngAfterViewInit anstelle von ngOnInit
user3666653

1
Dies ist das "versteckteste" Problem, das auftreten kann, danke für die Lösung! Dokumentationen hätten davor warnen können
Raycherr

34

Der Name matColumnDef und der tatsächliche Wert * matCellDef sollten identisch sein

Beispiel:

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

In meinem Fall ist oppNo für matColumnDef name und * matCellDef name gleich und die Sortierung funktioniert einwandfrei.


Interessant. Das war auch bei mir der Fall. Aber kennen Sie die tatsächlichen Gründe dafür oder ist das tatsächlich eine Art "Fehler"?
ReturnTable

22

Das Hinzufügen einer Sortierung innerhalb des Timeout-Blocks funktioniert für mich.

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

Wenn Sie keine Lifecykle-Haken verwenden möchten.


1
dummer Hack, aber es funktioniert, eine Idee, warum es ohne Timeout nicht funktioniert?
Ruben

Ich habe viel zu lange damit verbracht, alles andere auszuprobieren, weil ich dachte, ich würde verrückt. Lief wie am Schnürchen!
Willpnw

4
Wirklich eine schlechte Art, es zu tun. Es funktioniert, weil Sie nach der Initialisierung der Komponente einige Zeit verstreichen lassen, damit dataSource erstellt wird, und dann sort und paginator hinzufügen. Am besten verschieben Sie das Datenquellengebäude in ngOnInit und anschließend die Sortier- und Paginatorzuweisungen in AfterViewInit. Dafür gibt es Lifecycle-Hooks.
Selam Getachew

20

Ich habe auch dieses Problem getroffen. Da Sie auf die Definition des AfterViewInituntergeordneten Elements warten müssen, müssen Sie es implementieren und verwenden , nicht onInit.

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

Genial ! Danke
Shashank Vivek

Ich benutze eine Tabelle mit Sortieren, Filtern und Paginieren. Haben Sie eine Ahnung, warum nur die Sortierung definiert werden muss ngAfterViewInit? Der Rest arbeitete von ngOnInit. Es ist nur um zu versuchen zu verstehen, es ist dank dir behoben
Nicolas M.

14

Ich habe Stunden mit diesem Thema verbracht. Nachdem ich einige Themen durchgelesen habe, sind hier die Schritte, die ich ausgeführt habe.

  1. Wie bereits erwähnt , müssen Sie importieren MatSortModule.
  2. Stellen Sie sicher, dass Sie die Tabelle NICHT in a einschließen *ngIf. Ändern Sie ihn auf [hidden]als @zerg empfohlen . (Ich verstehe nicht warum)

Hoffe das hilft.


Es hat meinen Tag verschwendet, um das Problem herauszufinden, und dumm zeigt keinen Fehler.
Surekha Shelake

11

Meine Lösung bestand darin, mehrere Probleme zu beheben (im Grunde genommen wurden die meisten Lösungen auf dieser Seite zusammengeführt).

Dinge zu überprüfen:

  1. BrowserModule, MatTableModule, MatSortModule Module sollten in die Root-Modul-Datei importiert werden.
  2. Stellen Sie sicher, dass Sie MatTableDatasourceclass verwendet haben, und übergeben Sie Ihr Datenarray als Parameter
  3. Stellen Sie sicher, dass Ihre Tabelle nicht in einer *ngIf=....Direktive verschachtelt ist . Verwenden Sie stattdessen andere bedingte Operationen (verstehen Sie immer noch nicht warum).

3

Für mich hat das Ersetzen von * ngIf durch das Attribut [hidden] für das Mat-Table-Tag funktioniert. Wie kann ich diesen als Fehler in der Angular Material-Community veröffentlichen?


3

Ich habe dies in meinem Szenario behoben, indem ich die Tabellendaten mit demselben Namen wie * matColumnDef benannt habe. Beispiel:

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

Stattdessen

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

Es gab 2 Probleme für mich.

  1. Die Namen matColumnDef und matCellDef -> sind unterschiedlich
  2. Ich habe die Daten vom Dienst erhalten. Die Sortierung ngOnInit funktionierte nicht. Ersetzt mit

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


2

Ich habe diesen alten Blog gefunden, der mir geholfen hat, ihn zum Laufen zu bringen: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Stellen Sie sicher, dass Sie importieren MatSortModule
  2. Geben Sie den matSortHeader an
  3. Stellen Sie sicher, dass Ihre Datenquelle in a eingeschlossen ist MatTableDataSource
    • Dies ist derjenige, der mir geholfen hat, es zu klären (bekommen Sie es? Sortieren Sie es). In der Vorlage habe ich direkt auf das Array <table mat-table [dataSource]="this.products" matSort>verwiesen ( ), aber ich hätte das Datenquellenobjekt verwenden sollen, das ich im Code ( <table mat-table [dataSource]="this.dataSource" matSort>) initialisiert habe . Die Datenquelle wird wie folgt initialisiertdataSource = new MatTableDataSource(this.products)
  4. Teilen Sie der Datenquelle Ihre Sortierung in ngOnInit/ mitngAfterViewInit
  5. Schreiben Sie Ihre eigene Sorte, wenn Sie nicht verwenden möchten MatTableDataSource

1

Wenn sich Ihre Tabelle in einem * ngIf befindet und Sie der Meinung sind, dass dies damit zu tun hat, dass Ihre Tabelle nicht sortiert wird sortingDataAccessor, kann das Problem durch Angabe Ihrer eigenen Funktion wie bei mir behoben werden . Ich habe meine Tabelle in ein paar * ngIfs und es machte keinen Sinn, sie aus diesen * ngIfs herauszunehmen:

`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

Einer der Gründe, warum MatSort möglicherweise nicht funktioniert, besteht darin, dass es einer dataSource (dh this.dataSource.sort = this.sort) hinzugefügt wird, bevor es definiert wird. Dafür kann es mehrere Gründe geben:

  1. Wenn Sie die Sortierung in ngOnInit hinzufügen. Zu diesem Zeitpunkt ist die Vorlage noch nicht gerendert, sodass der MatSort, mit dem Sie arbeiten, @ViewChild(MatSort, { static: true }) sort: MatSort;undefiniert ist und verständlicherweise nichts tut. Eine Lösung für dieses Problem besteht darin, this.dataSource.sort = sortzu ngAfterViewInit zu wechseln. Wenn ngAfterViewInit aufgerufen wird, wird Ihre Komponente gerendert und MatSort sollte definiert werden.

  2. Wenn Sie * ngIf verwenden, ist dies Ihre Vorlage für Ihr Tabellenelement oder eine, wenn es sich um übergeordnete Elemente handelt. * ngIf bewirkt, dass Ihre Tabelle in dem Moment, in dem Sie versuchen, MatSort festzulegen, nicht gerendert wird. Zum Beispiel, wenn Sie *ngIf="dataSource.data.length > 0"auf Ihrem Tabellenelement haben (um es nur zu rendern, wenn Daten vorhanden sind) und Sie this.dataSource.sort = this.sortdirekt nach dem Festlegen this.dataSource.dataIhrer Daten festlegen . Die Komponentenansicht wird noch nicht neu gerendert, sodass MatSort weiterhin undefiniert ist.

Um MatSort zur Arbeit zu kommen und noch Ihren Tisch bedingt zeigen Sie könnten entscheiden , die zu ersetzen , *ngIfmit [hidden]wie in mehreren anderen Antworten angegeben. Wenn Sie jedoch Ihre * ngIf-Anweisung beibehalten möchten, können Sie die folgende Lösung verwenden. Diese Lösung funktioniert für Angular 9, ich habe sie in früheren Versionen nicht getestet, daher bin ich mir nicht sicher, ob sie dort funktioniert.

Ich habe diese Lösung hier gefunden: https://github.com/angular/components/issues/10205

Anstatt zu setzen:

@ViewChild(MatSort) sort: MatSort;

Verwenden Sie einen Setter für matSort. Dieser Setter wird ausgelöst, sobald sich matSort in Ihrer Ansicht ändert (dh beim ersten Mal definiert wird). Er wird nicht ausgelöst, wenn Sie Ihre Sortierung durch Klicken auf die Pfeile ändern. Das wird so aussehen:

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

Wenn Sie andere Funktionen haben, die die Sortierung (programmgesteuert) ändern, bin ich mir nicht sicher, ob sie erneut ausgelöst wird. Ich habe dies nicht getestet. Wenn Sie nicht sicherstellen möchten, dass die Sortierung nur festgelegt wird, wenn die Sortierung undefiniert war, können Sie Folgendes tun:

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

0

Überprüfen Sie, ob in der Konsole Javascript-Fehler vorliegen. Es kann sein, dass etwas anderes fehlgeschlagen ist, bevor Ihre Sortierung initialisiert wurde.

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.