Demo di smistamento mat-table non funziona


107

Sto cercando di far funzionare l' mat-tableordinamento localmente e, sebbene riesca a visualizzare i dati come previsto, facendo clic sulla riga di intestazione non viene eseguito l'ordinamento come negli esempi online (non accade assolutamente nulla). Sto cercando di far funzionare questa demo localmente: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

Ho generato un nuovo progetto con Angular CLI, quindi ho seguito questi passaggi: https://material.angular.io/guide/getting-started

Ecco i miei file locali:

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

Qualcuno ha un'idea del motivo per cui dovrebbe apparire come la tabella online ma manca la funzionalità di ordinamento?


Prima eseguo il debug dell'app. Eventuali errori? eseguire il ng test --sm=falsee vedere cosa viene fuori.
k.vincent

Funziona per me senza @ViewChild (MatSort) sort: MatSort; Qualche ragione ?
user123456

Risposte:


198

Per chiunque altro possa avere questo problema: il problema era che non avevo letto correttamente il riferimento API sul sito Web dei materiali angolari, la parte che diceva che dovevo importare MatSortModule. Dopo aver modificato il mio elenco di importazioni in app.module.ts in

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

ha funzionato bene


45
non c'è menzione di questo modulo nella documentazione. material.angular.io/components/table/overview#sorting ho perso un'ora anche su questo.
Sonic Soul

8
questo va bene, nel testo dell'intestazione è cliccabile e anche l'icona è lì, ma l'ordinamento non funziona.
SPnL

2
Controlla se BrowserAnimationsModuleè importato anche in app.module.ts
Augustas

2
Posso dire che sono SOB? Ho passato 1 ora a cercare di capire perché il mio ViewChild non funzionava. Non possono importare / esportare questo MatSortModule da MatTableModule ??
Sampgun

7
Ho importato MatSortModulee BrowserAnimationsModule, e mi sono assicurato che il valore matColumnDef corrisponda al nome della proprietà, ma non sono ancora in grado di farlo fare nulla.
Trevor

133

Ho avuto un problema che la funzione di ordinamento funzionava ma non si ordinava correttamente. Mi sono reso conto che matColumnDefdeve avere lo stesso nome della mia proprietà a class / interfacecui mi riferisco matCellDef.

Secondo la documentazione del materiale angolare :

Per impostazione predefinita, MatTableDataSource esegue l'ordinamento partendo dal presupposto che il nome della colonna ordinata corrisponda al nome della proprietà dei dati visualizzato nella colonna.

Per esempio:

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

Il namenella matColumnDefdirettiva deve essere lo stesso nameutilizzato nel <mat-cell>componente.


1
A cosa ti riferisci nel tuo esempio? Sarebbe utile vedere anche la tua interfaccia, per il confronto.
isherwood

1
Stavo usando "Id" come nome della colonna mentre l'entità aveva "id". La differenza del caso era che non funzionava (a causa di un errore di refactoring) .. Ora è risolto. Grazie
NitinSingh

2
Grazie, è molto utile.
Bohao LI

2
@NitinSingh, e se avessi bisogno di chiamare una funzione sul element, come questo `{{row.getName ()}}`
codentario del

1
Ti devo totalmente una birra perché sono stato bloccato su questo problema per un po 'e questo commento ha risolto il mio problema.
noel

99

Se la tabella è all'interno di * ngIf, non funzionerà. Funzionerà se viene modificato in [nascosto]


33
!!! SALVATE LA MIA GIORNATA !!! Utilizzare invece <div *ngIf="xxx"> del<div [hidden]="!xxx">
Mark

1
Posso confermare, anche questo ha funzionato per me. Grazie zerg!
chiusura

1
Grazie mille, questo mi è costato così tanto tempo !!
themightylc

1
O imposta semplicemente l'origine dati in ngAfterViewInit invece di ngOnInit
user3666653

1
Questo è il problema più "nascosto" che potrebbe accadere, grazie per la soluzione! la documentazione avrebbe potuto mettere in guardia su questo
Raycherr

35

Il nome matColumnDef e il nome del valore effettivo * matCellDef devono essere uguali

Esempio:

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

Nel mio caso oppNo è lo stesso per il nome matColumnDef e * il nome matCellDef e l'ordinamento funziona bene.


Interessante. Anche per me è stato così. Ma conosci il vero ragionamento alla base di questo o è in realtà una sorta di "bug"?
Tabella di ritorno

22

L'aggiunta di ordinamento nel blocco di timeout funziona per me,

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

Se non vuoi usare i ganci lifecykle.


1
stupido trucco ma funziona, qualche idea del perché non funziona senza il timeout?
Ruben

Ho passato troppo tempo a provare tutto il resto, pensando che stavo impazzendo. Ha funzionato come un fascino!
willpnw

4
Davvero un brutto modo di farlo. Funziona perché stai lasciando passare un po 'di tempo dopo l'inizializzazione del componente in modo che dataSource venga creato, quindi aggiungi ordinamento e impaginatore. La cosa migliore è spostare il buidling datSource in ngOnInit, quindi spostare le assegnazioni di ordinamento e impaginazione in AfterViewInit. Questo è il motivo per cui esistono gli hook Lifecycle.
Selam Getachew

20

Ho anche riscontrato questo problema. Poiché è necessario attendere che il figlio venga definito, è necessario implementare e utilizzare AfterViewInit, non onInit.

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

Eccezionale ! Grazie
Shashank Vivek

Sto usando una tabella con ordinamento, filtraggio e impaginazione. Hai idea del perché debba essere definito solo l'ordinamento ngAfterViewInit? Il resto stava lavorando da ngOnInit. È solo per cercare di capire, è stato risolto grazie a te
Nicolas M.

14

Ho passato ore su questo problema. Dopo aver letto una serie di thread, ecco i passaggi che ho fatto.

  1. Come menzionato da @avern , devi importare MatSortModule.
  2. Assicurati di NON racchiudere la tabella in un file *ngIf. Modificalo [hidden]come consigliato da @zerg . (Non capisco perché)

Spero che questo ti aiuti.


Ha sprecato la mia giornata per scoprire il problema e stupido non mostra alcun errore.
surekha shelake

11

La mia soluzione era correggere diverse cose (fondamentalmente unendo la maggior parte delle soluzioni in questa pagina).

Cose da controllare:

  1. BrowserModule, MatTableModule, MatSortModule I moduli devono essere importati nel file dei moduli radice.
  2. Assicurati di aver usato la MatTableDatasourceclasse e di passarci il tuo array di dati come parametro
  3. Assicurati che la tua tabella non sia annidata in una *ngIf=....direttiva. Usa invece altre operazioni condizionali (ancora non capisco perché).

3

Per me la sostituzione di * ngIf con l'attributo [nascosto] per il tag mat-table ha funzionato. Come pubblicare questo come bug nella comunità di Angular Material?


3

Ho risolto questo problema nel mio scenario nominando i dati della tabella con lo stesso nome di * matColumnDef Ad esempio:

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

Anziché

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

Ci sono stati 2 problemi per me.

  1. I nomi matColumnDef e matCellDef -> sono diversi
  2. Ricevo i dati dal servizio. L'ordinamento ngOnInit non funzionava. Sostituito con

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


2

Ho trovato questo vecchio blog che mi ha aiutato a farlo funzionare: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Assicurati di importare MatSortModule
  2. Specifica l' matSortintestazione
  3. Assicurati di racchiudere la tua origine dati in un file MatTableDataSource
    • Questo è quello che mi ha aiutato a risolverlo (capirlo? Risolverlo ). Nel template mi riferivo direttamente all'array ( <table mat-table [dataSource]="this.products" matSort>) ma avrei dovuto usare l'oggetto datasource che ho inizializzato nel code ( <table mat-table [dataSource]="this.dataSource" matSort>). L'origine dati è inizializzata comedataSource = new MatTableDataSource(this.products)
  4. Indica alla fonte dati il ​​tuo ordinamento, in ngOnInit/ngAfterViewInit
  5. Scrivi il tuo tipo, se non vuoi usarlo MatTableDataSource

1

Se la tua tabella è all'interno di un * ngIf e pensi che abbia qualcosa a che fare con il fatto che non ordina la tua tabella, specificare la tua sortingDataAccessorfunzione potrebbe risolvere il problema come ha fatto per me. Ho il mio tavolo all'interno di un paio di * ngIfs e toglierlo da quei * ngIfs non aveva senso:

`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

Uno dei motivi per cui MatSort potrebbe non funzionare è quando viene aggiunto a un dataSource (cioè this.dataSource.sort = this.sort) prima che sia definito. Le ragioni possono essere molteplici:

  1. se aggiungi l'ordinamento in ngOnInit. A questo punto il modello non è ancora renderizzato, quindi il MatSort che ottieni @ViewChild(MatSort, { static: true }) sort: MatSort;non è definito e comprensibilmente non farà nulla. Una soluzione per questo problema è passare this.dataSource.sort = sorta ngAfterViewInit. Quando ngAfterViewInit viene chiamato, viene eseguito il rendering del componente e deve essere definito MatSort.

  2. quando usi * ngIf è il tuo modello sul tuo elemento tabella o uno se è elementi padre e questo * ngIf fa sì che la tua tabella non venga visualizzata nel momento in cui provi a impostare MatSort. Ad esempio, se hai *ngIf="dataSource.data.length > 0"l'elemento della tabella (per renderlo solo se sono presenti dati) e lo imposti this.dataSource.sort = this.sortsubito dopo aver impostato this.dataSource.datacon i tuoi dati. La visualizzazione dei componenti non verrà ancora riesaminata, quindi MatSort sarà ancora indefinito.

Per far funzionare MatSort e mostrare comunque la tua tabella in modo condizionale, potresti decidere di sostituire il *ngIfcon [hidden]come indicato in più altre risposte. Tuttavia, se si desidera mantenere l'istruzione * ngIf, è possibile utilizzare la seguente soluzione. Questa soluzione funziona per Angular 9, non l'ho testata su versioni precedenti, quindi non sono sicuro che funzioni lì.

Ho trovato questa soluzione qui: https://github.com/angular/components/issues/10205

Invece di mettere:

@ViewChild(MatSort) sort: MatSort;

usa un setter per matSort. Questo setter si attiverà una volta che matSort nella tua vista cambia (cioè è definito la prima volta), non si attiverà quando cambi il tuo ordinamento facendo clic sulle frecce. Questo sarà simile a questo:

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

Se hai altre funzioni che (a livello di programmazione) cambiano l'ordinamento, non sono sicuro che si attiverà di nuovo, non l'ho testato. Se non vuoi assicurarti che imposti l'ordinamento solo se l'ordinamento non era definito, puoi fare qualcosa del genere:

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

0

Verifica se hai errori javascript nella console. Potrebbe essere che qualche altra cosa non sia riuscita prima che l'ordinamento fosse inizializzato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.