Demo sortowania mat-table nie działa

108

Staram się, aby mat-tablesortowanie działało lokalnie i chociaż mogę sprawić, by dane były wyświetlane zgodnie z oczekiwaniami, kliknięcie wiersza nagłówka nie powoduje sortowania, tak jak w przypadku przykładów online (w ogóle nic się nie dzieje). Próbuję, aby to demo działało lokalnie: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

Wygenerowałem nowy projekt za pomocą Angular CLI, a następnie wykonałem następujące kroki: https://material.angular.io/guide/getting-started

Oto moje pliki lokalne:

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

Czy ktoś ma pomysł, dlaczego miałby wyglądać jak tabela online, ale brakuje mu funkcji sortowania?

avern
źródło
Najpierw debugowałbym aplikację. Jakieś błędy? uruchom ng test --sm=falsei zobacz, co wyjdzie.
k.vincent
U mnie działa bez @ViewChild (MatSort) sort: MatSort; Jakiegokolwiek powodu ?
user123456

Odpowiedzi:

201

Dla każdego, kto może mieć ten problem: Problem polegał na tym, że nie przeczytałem poprawnie referencji API na stronie materiałów kątowych, w części, która mówi, że muszę zaimportować MatSortModule. Po zmianie listy importów w app.module.ts na

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

działało dobrze

avern
źródło
45
nie ma wzmianki o tym module w dokumentacji. material.angular.io/components/table/overview#sorting Ja również zmarnowałem na to godzinę.
Sonic Soul
8
to jest w porządku, w nagłówku tekst można kliknąć, a ikona również tam jest, ale sortowanie nadal nie działa.
SPnL
3
Sprawdź, czy BrowserAnimationsModulejest również importowany w app.module.ts
Augustas
2
Czy mogę powiedzieć, że to SOB? Spędziłem godzinę, próbując dowiedzieć się, dlaczego mój ViewChild nie działa. Czy nie mogą importować / eksportować tego MatSortModule z MatTableModule?
Sampgun
7
Zaimportowałem MatSortModulei BrowserAnimationsModulei upewniłem się, że wartość matColumnDef jest zgodna z nazwą właściwości, ale nadal nie jestem w stanie nic zrobić.
Trevor
136

Miałem problem, że funkcja sortowania działa, ale nie sortowała prawidłowo. Zdałem sobie sprawę, że matColumnDefmusi mieć taką samą nazwę właściwości mojej class / interface, w której się odwołuję matCellDef.

Zgodnie z dokumentacją Angular Material :

Domyślnie MatTableDataSource sortuje przy założeniu, że nazwa posortowanej kolumny jest zgodna z nazwą właściwości danych wyświetlaną w kolumnie.

Na przykład:

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

nameW matColumnDefdyrektywie musi być takie same jak nameużywane w <mat-cell>komponencie.

Andre Evangelista
źródło
1
Co w swoim przykładzie nawiązujesz? Dla porównania pomocne byłoby również obejrzenie interfejsu.
isherwood
1
Użyłem „Id” jako nazwy kolumny, podczas gdy jednostka miała „id”. Różnica polegała na tym, że nie działał (z powodu chybienia w refaktoryzacji). Teraz jest rozwiązany. Dzięki
NitinSingh
2
Dziękuję, to bardzo przydatne.
Bohao LI
2
@NitinSingh, a co jeśli potrzebujesz wywołać funkcję element, taką jak ten `{{row.getName ()}}`
codentary
2
Jestem całkowicie winien piwo, ponieważ utknąłem na tym problemie przez jakiś czas i ten komentarz rozwiązał mój problem.
noel
101

Jeśli tabela znajduje się wewnątrz * ngIf, nie będzie działać. Będzie działać, jeśli zostanie zmieniony na [ukryty]

ayhtut
źródło
34
!!! OSZCZĘDZASZ MÓJ DZIEŃ !!! Użyj zamiast <div *ngIf="xxx"> z<div [hidden]="!xxx">
Mark
1
Potwierdzam, że to zadziałało również dla mnie. Dzięki zerg!
clo5ure
1
Dziękuję bardzo, kosztowało mnie to tyle czasu !!
themightylc
1
Lub po prostu ustaw źródło danych w ngAfterViewInit zamiast ngOnInit
user3666653
1
Jest to najbardziej „ukryty” problem, jaki może się zdarzyć, dzięki za rozwiązanie! dokumentacje mogły o tym
ostrzec
36

Nazwa matColumnDef i rzeczywista nazwa wartości * matCellDef powinny być takie same

Przykład:

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

W moim przypadku oppNo jest takie samo dla nazwy matColumnDef i nazwy * matCellDef, a sortowanie działa poprawnie.

Siddu
źródło
Ciekawy. Tak też było w przypadku mnie. Ale czy znasz rzeczywiste powody tego, czy jest to właściwie jakiś „błąd”?
ReturnTable
22

Dodanie sortowania w ramach limitu czasu działa dla mnie,

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

Jeśli nie chcesz używać haczyków lifecykle.

Chandrahasan
źródło
1
głupi hack, ale działa, jakiś pomysł, dlaczego nie działa bez limitu czasu?
Ruben
Spędziłem o wiele za długo próbując wszystkiego innego, myśląc, że oszaleję. Działał jak urok!
willpnw
4
Naprawdę zły sposób na zrobienie tego. To działa, ponieważ pozwalasz upłynąć trochę czasu po inicjalizacji komponentu, aby zbudować DataSource, a następnie dodajesz sortowanie i paginator. Najlepszym rozwiązaniem jest przeniesienie biblioteki datSource w ngOnInit, a następnie przeniesienie przypisań sortowania i stronicowania w AfterViewInit. Po to istnieją haki Lifecycle.
Selam Getachew
21

Trafiłem też w ten problem. Ponieważ musisz poczekać na zdefiniowanie dziecka, musisz implementować i używać AfterViewInit, a nie onInit.

  ngAfterViewInit (){
    this.dataSource.sort = this.sort;
  }
flashape
źródło
Niesamowite ! Dzięki
Shashank Vivek
Używam tabeli z sortowaniem, filtrowaniem i paginacją. Czy masz jakąś wskazówkę, dlaczego należy zdefiniować tylko sortowanie ngAfterViewInit? Reszta pracowała od ngOnInit. To tylko próba zrozumienia, to naprawione dzięki tobie
Nicolas M.
14

Spędziłem godziny nad tą kwestią. Po przeczytaniu kilku wątków, oto kroki, które zrobiłem.

  1. Jak wspomniał @avern , musisz zaimportować MatSortModule.
  2. Upewnij się, że NIE zamykasz tabeli w *ngIf. Zmień go [hidden]jako @zerg zalecane . (Nie rozumiem dlaczego)

Mam nadzieję że to pomoże.

berrytchaks
źródło
Zmarnowałem mój dzień, aby znaleźć problem, a głupi nie pokazuje żadnego błędu.
surekha shelake
11

Moim rozwiązaniem było naprawienie kilku rzeczy (w zasadzie scalenie większości rozwiązań na tej stronie).

Rzeczy do sprawdzenia:

  1. BrowserModule, MatTableModule, MatSortModule Moduły należy importować do głównego pliku modułów.
  2. Upewnij się, że użyłeś MatTableDatasourceklasy i przekaż w niej swoją tablicę danych jako parametr
  3. Upewnij się, że tabela nie jest zagnieżdżona w *ngIf=....dyrektywie. Zamiast tego użyj innych operacji warunkowych (nadal nie rozumiem, dlaczego).
Samuel Mutemi
źródło
3

U mnie zastąpienie * ngIf atrybutem [hidden] dla tagu mat-table zadziałało. Jak opublikować ten jako błąd w społeczności Angular Material?

Gaurav Kohirkar
źródło
3

Naprawiłem to w moim scenariuszu, nazywając dane tabeli o tej samej nazwie co * matColumnDef Na przykład:

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

Zamiast

<!-- 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>
speksy
źródło
3

Były dla mnie 2 problemy.

  1. Nazwy matColumnDef i matCellDef -> są różne
  2. Otrzymywałem dane z usługi. Sortowanie ngOnInit nie działało. Zastąpiony

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

Raj kannan Iyyappan
źródło
2

Znalazłem starego bloga, który pomógł mi w uruchomieniu go: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Pamiętaj, aby zaimportować MatSortModule
  2. Określ matSortnagłówek
  3. Pamiętaj, aby opakować swoje źródło danych w plik MatTableDataSource
    • To jest ten, który pomógł mi rozwiązać to (get it? Sort it out). W szablonie odnosiłem się bezpośrednio do tablicy ( <table mat-table [dataSource]="this.products" matSort>), ale powinienem był użyć obiektu źródła danych, który zainicjowałem w kodzie ( <table mat-table [dataSource]="this.dataSource" matSort>). Źródło danych jest inicjowane jakdataSource = new MatTableDataSource(this.products)
  4. Poinformuj źródło danych o swoim rodzaju w ngOnInit/ngAfterViewInit
  5. Napisz swój własny rodzaj, jeśli nie chcesz używać MatTableDataSource
kumaheiyama
źródło
1

Jeśli twoja tabela znajduje się wewnątrz * ngI i myślisz, że ma to coś wspólnego z tym, że nie sortuje twojej tabeli, określenie własnej sortingDataAccessorfunkcji może rozwiązać problem, tak jak w przypadku mnie. Mam swój stół w kilku * ngIfs i wyjęcie go z tych * ngIfs nie miało sensu:

`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;
    }
}`
O. MeeKoh
źródło
1

Jednym z powodów, dla których MatSort może nie działać, jest dodanie go do źródła danych (tj. this.dataSource.sort = this.sort) Przed jego zdefiniowaniem. Przyczyn może być wiele:

  1. jeśli dodasz sortowanie w ngOnInit. W tym momencie szablon nie jest jeszcze renderowany, więc MatSort, który otrzymujesz, @ViewChild(MatSort, { static: true }) sort: MatSort;jest niezdefiniowany i zrozumiałe, że nic nie zrobi. Rozwiązaniem tego problemu jest przejście this.dataSource.sort = sortdo ngAfterViewInit. Gdy wywoływana jest funkcja ngAfterViewInit, renderowany jest komponent i należy zdefiniować MatSort.

  2. kiedy używasz * ngIf jest twoim szablonem w elemencie tabeli lub takim, jeśli jest to element nadrzędny, a to * ngIf powoduje, że twoja tabela nie jest renderowana w momencie próby ustawienia MatSort. Na przykład, jeśli masz *ngIf="dataSource.data.length > 0"element tabeli (aby renderować go tylko wtedy, gdy są obecne dane) i ustawiłeś this.dataSource.sort = this.sortzaraz po ustawieniu this.dataSource.dataz danymi. Widok komponentu nie zostanie jeszcze zrenderowany, więc MatSort nadal będzie niezdefiniowany.

Aby uzyskać MatSort do pracy i nadal warunkowo pokazać swoją tabelę można zdecydować się wymienić *ngIfz [hidden]jak stwierdzono w wielu innych odpowiedzi. Jeśli jednak chcesz zachować swoje oświadczenie * ngIf, możesz skorzystać z następującego rozwiązania. To rozwiązanie działa na Angular 9, nie testowałem go na poprzednich wersjach, więc nie jestem pewien, czy tam działa.

Znalazłem to rozwiązanie tutaj: https://github.com/angular/components/issues/10205

Zamiast wstawiać:

@ViewChild(MatSort) sort: MatSort;

użyj setera dla matSort. Ten seter uruchomi się, gdy matSort w twoim widoku zmieni się (tj. Zostanie zdefiniowany po raz pierwszy), nie zostanie uruchomiony, gdy zmienisz sortowanie, klikając strzałki. Będzie to wyglądać tak:

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

Jeśli masz inne funkcje, które (programowo) zmieniają sortowanie, nie jestem pewien, czy znowu się uruchomią, nie testowałem tego. Jeśli nie chcesz się upewnić, że ustawia sortowanie tylko wtedy, gdy sortowanie było niezdefiniowane, możesz zrobić coś takiego:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    if (!this.dataSource.sort) {
        this.dataSource.sort = sort;
    }
}
Emmy
źródło
0

Sprawdź, czy masz jakieś błędy javascript w konsoli. Może się zdarzyć, że coś innego nie powiodło się przed zainicjowaniem sortowania.

Ε Г И І И О
źródło
0

Właściwie nazwa matColumnDef (tj. Nazwa kolumny) i nazwa właściwości klasy / interfejsu powinny być takie same, aby działało.

Czasami nie możemy zmienić nazwy naszej właściwości klasy / interfejsu, w takim przypadku możemy zaimplementować niestandardowe sortowanie, jak poniżej.

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

jeśli wykonamy sortowanie w kolumnie „id”, to nie zadziała. Spróbuj z niestandardowym sortowaniem

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];
        }
}
kasee nadh reddy bojja
źródło