Estou tentando fazer a mat-table
classificação funcionar localmente e, embora possa fazer com que os dados sejam exibidos conforme o esperado, clicar na linha do cabeçalho não faz a classificação como nos exemplos online (nada acontece). Estou tentando fazer com que esta demonstração funcione localmente:
https://material.angular.io/components/sort/overview
https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview
Eu gerei um novo projeto com Angular CLI, então segui estas etapas: https://material.angular.io/guide/getting-started
Aqui estão meus arquivos locais:
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 */
Alguém tem uma ideia de por que ele apareceria como a mesa online, mas não tem a funcionalidade de classificação?
fonte
ng test --sm=false
e veja o que está saindo.Respostas:
Para qualquer outra pessoa que possa ter esse problema: O problema é que eu não li a referência da API corretamente no site de materiais do angular, a parte que dizia que eu precisava importar MatSortModule. Depois de alterar minha lista de importações em app.module.ts para
funcionou bem
fonte
BrowserAnimationsModule
também é importado em app.module.tsMatSortModule
eBrowserAnimationsModule
e assegurei-me de que o valor matColumnDef corresponde ao nome da propriedade, mas ainda não consigo fazer nada com ele.Eu tive um problema que a função de classificação estava funcionando, mas não estava classificando corretamente. Percebi que
matColumnDef
tem que ter o mesmo nome da propriedade minha àclass / interface
qual estou me referindomatCellDef
.De acordo com a documentação do Material Angular :
Por exemplo:
O
name
namatColumnDef
diretiva deve ser o mesmo que oname
usado no<mat-cell>
componente.fonte
element
, como este `{{row.getName ()}}`Se a tabela estiver dentro de * ngIf, não funcionará. Funcionará se for alterado para [oculto]
fonte
<div *ngIf="xxx">
a<div [hidden]="!xxx">
O nome matColumnDef e * o nome do valor real matCellDef devem ser iguais
Exemplo:
No meu caso, oppNo é o mesmo para o nome matColumnDef e o nome * matCellDef e a classificação está funcionando bem.
fonte
Adicionar classificação dentro do bloco de tempo limite funciona para mim,
Se você não quiser usar ganchos de ciclo de vida.
fonte
Eu também acertei esse problema. Já que você precisa esperar que o filho seja definido, você deve implementar e usar
AfterViewInit
, não onInit.fonte
ngAfterViewInit
? O resto estava trabalhandongOnInit
. É só tentar entender, está consertado graças a vocêPassei horas nesse assunto. Depois de ler vários tópicos, aqui estão as etapas que fiz.
MatSortModule
.*ngIf
. Alterá-lo para[hidden]
como @zerg recomendado . (Não entendo porque)Espero que isto ajude.
fonte
Minha solução foi consertar várias coisas (basicamente mesclando a maioria das soluções nesta página).
Coisas a verificar:
BrowserModule, MatTableModule, MatSortModule
Os módulos devem ser importados no arquivo de módulos raiz.MatTableDatasource
classe e passar seu array de dados como um parâmetro*ngIf=....
diretiva. Use outras operações condicionais (ainda não entendo por quê).fonte
Para mim, substituir * ngIf pelo atributo [oculto] para a tag mat-table funcionou. Como postar isso como um bug para a comunidade Angular Material?
fonte
Corrigi isso em meu cenário nomeando os dados da tabela com o mesmo nome de * matColumnDef. Por exemplo:
Em vez de
fonte
Houve 2 problemas para mim.
Estava recebendo os dados do serviço. A classificação ngOnInit não estava funcionando. Substituído por
ngAfterViewInit () {this.dataSource.sort = this.sort; }
fonte
Encontrei este blog antigo que me ajudou a fazê-lo funcionar: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort
MatSortModule
matSort
cabeçalhoMatTableDataSource
<table mat-table [dataSource]="this.products" matSort>
), mas deveria ter usado o objeto de fonte de dados que inicializei no código (<table mat-table [dataSource]="this.dataSource" matSort>
). A fonte de dados é inicializada comodataSource = new MatTableDataSource(this.products)
ngOnInit
/ngAfterViewInit
MatTableDataSource
fonte
Se sua tabela estiver dentro de um * ngIf e você achar que isso tem algo a ver com o fato de não classificar sua mesa, então especificar sua própria
sortingDataAccessor
função pode resolver o problema como fez para mim. Eu tenho minha tabela dentro de alguns * ngIfs e retirá-la desses * ngIfs não fazia sentido:fonte
Uma das razões pelas quais MatSort pode não funcionar é quando ele é adicionado a um dataSource (ou seja,
this.dataSource.sort = this.sort
) antes de ser definido. Pode haver vários motivos para isso:se você adicionar a classificação em ngOnInit. Neste ponto, o modelo ainda não foi renderizado, então o MatSort que você obteve
@ViewChild(MatSort, { static: true }) sort: MatSort;
é indefinido e, compreensivelmente, não fará nada. Uma solução para esse problema é mudarthis.dataSource.sort = sort
para ngAfterViewInit. Quando ngAfterViewInit é chamado, seu componente é renderizado e MatSort deve ser definido.quando você usa * ngIf é seu modelo em seu elemento de tabela ou um se seus elementos pais e este * ngIf faz com que sua tabela não seja renderizada no momento em que você tenta definir o MatSort. Por exemplo, se você tiver
*ngIf="dataSource.data.length > 0"
em seu elemento de tabela (para renderizá-lo apenas se houver dados presentes) e você definirthis.dataSource.sort = this.sort
logo após definirthis.dataSource.data
com seus dados. A visualização do componente ainda não será renderizada, então MatSort ainda ficará indefinido.Para que o MatSort funcione e ainda mostre condicionalmente a sua tabela, você pode decidir substituir o
*ngIf
por[hidden]
conforme indicado em várias outras respostas. No entanto, se você deseja manter sua instrução * ngIf, pode usar a solução a seguir. Esta solução funciona para Angular 9, eu não testei em versões anteriores, então não tenho certeza se funciona lá.Encontrei esta solução aqui: https://github.com/angular/components/issues/10205
Em vez de colocar:
use um setter para matSort. Este setter irá disparar assim que matSort em sua visão mudar (ou seja, for definido pela primeira vez), ele não irá disparar quando você mudar sua classificação clicando nas setas. Será parecido com este:
Se você tiver outras funções que (programaticamente) alteram a classificação, não tenho certeza se ele vai disparar novamente, eu não testei isso. Se você não quiser ter certeza de que só definirá a classificação se a classificação for indefinida, você pode fazer algo assim:
fonte
Veja se você tem algum erro de javascript no console. Pode ser que alguma outra coisa falhou antes da inicialização da classificação.
fonte