La demostración de clasificación de la mesa de tapetes no funciona

107

Estoy tratando de que la mat-tableclasificación funcione localmente, y aunque puedo hacer que los datos se muestren como se esperaba, hacer clic en la fila del encabezado no realiza la clasificación como lo hace en los ejemplos en línea (no sucede nada). Estoy tratando de que esta demostración funcione localmente: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

He generado un nuevo proyecto con Angular CLI, luego seguí estos pasos: https://material.angular.io/guide/getting-started

Aquí están mis archivos locales:

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

¿Alguien tiene una idea de por qué aparecería como la tabla en línea pero carece de la funcionalidad de clasificación?

Avern
fuente
Primero depuraría la aplicación. ¿Algún error? ejecute el ng test --sm=falsey vea lo que sale.
k.vincent
Me funciona sin @ViewChild (MatSort) sort: MatSort; Cualquier razón ?
user123456

Respuestas:

198

Para cualquier otra persona que pueda tener este problema: El problema fue que no leí correctamente la referencia de API en el sitio web de materiales angulares, la parte que decía que tenía que importar MatSortModule. Después de cambiar mi lista de importaciones en app.module.ts a

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

funcionó bien

Avern
fuente
45
no hay mención de este módulo en la documentación. material.angular.io/components/table/overview#sorting perdí una hora en esto también.
Sonic Soul
8
esto está bien, en el texto del encabezado se puede hacer clic y el icono también está allí, pero la clasificación no funciona.
SPnL
2
Compruebe si BrowserAnimationsModuletambién se importa en app.module.ts
Augustas
2
¿Puedo decir que son hijos de puta? Pasé 1 hora tratando de averiguar por qué mi ViewChild no estaba funcionando. ¿No pueden importar / exportar este MatSortModule desde MatTableModule?
Sampgun
7
He importado MatSortModuley BrowserAnimationsModule, y me he asegurado de que el valor de matColumnDef coincida con el nombre de la propiedad, pero todavía no puedo hacer nada.
Trevor
133

Tuve un problema de que la función de clasificación estaba funcionando pero no estaba clasificando correctamente. Me di cuenta de que matColumnDeftiene que tener el mismo nombre de la propiedad de mi class / interfacereferencia en la que estoy haciendo referencia matCellDef.

Según la documentación de Angular Material :

De forma predeterminada, MatTableDataSource se ordena con el supuesto de que el nombre de la columna ordenada coincide con el nombre de la propiedad de datos que muestra la columna.

Por ejemplo:

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

El contenido namede la matColumnDefdirectiva debe ser el mismo que el nameutilizado en el <mat-cell>componente.

André Evangelista
fuente
1
¿A qué te refieres en tu ejemplo? También sería útil ver su interfaz para comparar.
isherwood
1
Estaba usando "Id" como nombre de la columna mientras que la entidad tenía "id". La diferencia de caso fue que no se ejecutara (debido a una falla de refactorización). Ahora está resuelto. Gracias
NitinSingh
2
Gracias, es muy útil.
Bohao LI
2
@NitinSingh, ¿qué sucede si necesita llamar a una función en el element, como este `{{row.getName ()}}`
codentario
1
Te debo una cerveza porque he estado atascado en este problema por un tiempo y este comentario solucionó mi problema.
Noel
99

Si la tabla está dentro de * ngIf, no funcionará. Funcionará si se cambia a [oculto]

ayhtut
fuente
33
!!! TU SALVAS MI DIA !!! Utilizar en lugar de <div *ngIf="xxx"> la<div [hidden]="!xxx">
Marcos
1
Puedo confirmar que esto también funcionó para mí. ¡Gracias zerg!
cierre el
1
¡Muchas gracias, esto me ha costado tanto tiempo!
theightylc
1
O simplemente configure la fuente de datos en ngAfterViewInit en lugar de ngOnInit
user3666653
1
Este es el problema más "oculto" que podría suceder, ¡gracias por la solución! Las documentaciones podrían haber advertido sobre esto
Raycherr
35

El nombre de matColumnDef y el nombre del valor real * matCellDef deben ser iguales

Ejemplo:

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

En mi caso, oppNo es el mismo para el nombre matColumnDef y el nombre * matCellDef y la clasificación funciona bien.

Siddu
fuente
Interesante. Ese fue mi caso también. Pero, ¿conoce el razonamiento real detrás de esto o es en realidad una especie de "error"?
ReturnTable
22

Agregar ordenamiento dentro del bloque de tiempo de espera funciona para mí,

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

Si no quiere usar ganchos de vida.

Chandrahasan
fuente
1
truco estúpido pero funciona, ¿alguna idea de por qué no funciona sin el tiempo de espera?
Ruben
Pasé demasiado tiempo intentando todo lo demás, pensando que me estaba volviendo loco. ¡Trabajado como un encanto!
willpnw
4
Realmente una mala forma de hacerlo. Itworks porque está dejando pasar un tiempo después de la inicialización del componente para que se compile el origen de datos, y luego agrega sort y paginator. Lo mejor es mover el edificio datSource en ngOnInit y luego mover las asignaciones de ordenación y paginador en AfterViewInit. Para eso existen los ganchos de ciclo de vida.
Selam Getachew
20

También toqué este tema. Dado que debe esperar a que se defina el niño, debe implementar y usar AfterViewInit, no onInit.

  ngAfterViewInit (){
    this.dataSource.sort = this.sort;
  }
flashape
fuente
Increíble ! Gracias
Shashank Vivek
Estoy usando una tabla con clasificación, filtrado y paginación. ¿Tiene alguna idea de por qué solo se debe definir la clasificación ngAfterViewInit? El resto estaba trabajando desde ngOnInit. Es solo para tratar de entender, se solucionó gracias a ti
Nicolas M.
14

Pasé horas en este tema. Después de leer varios hilos, estos son los pasos que seguí.

  1. Como mencionó @avern , necesita importar MatSortModule.
  2. Asegúrese de NO encerrar la tabla en un *ngIf. Cambiarlo a [hidden]como @zerg recomienda . (No entiendo por qué)

Espero que esto ayude.

berrytchaks
fuente
Ha desperdiciado mi día para descubrir el problema y estúpido no muestra ningún error.
surekha shelake
11

Mi solución fue arreglar varias cosas (básicamente fusionando la mayoría de las soluciones en esta página).

Cosas para comprobar:

  1. BrowserModule, MatTableModule, MatSortModule Los módulos deben importarse en el archivo de módulos raíz.
  2. Asegúrese de haber usado la MatTableDatasourceclase y pasar su matriz de datos como parámetro
  3. Asegúrese de que su tabla no esté anidada en una *ngIf=....directiva. En su lugar, use otras operaciones condicionales (todavía no entiendo por qué).
Samuel Mutemi
fuente
3

Para mí, reemplazar * ngIf con el atributo [oculto] para la etiqueta mat-table funcionó. ¿Cómo publicar este como un error en la comunidad de Angular Material?

Gaurav Kohirkar
fuente
3

Arreglé esto en mi escenario al nombrar los datos de la tabla con el mismo nombre que * matColumnDef Por ejemplo:

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

En lugar

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

Hubo 2 problemas para mí.

  1. Los nombres matColumnDef y matCellDef -> son diferentes
  2. Estaba obteniendo los datos del servicio. La ordenación ngOnInit no funcionaba. Reemplazadas con

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

Raj kannan Iyyappan
fuente
2

Encontré este viejo blog que me ayudó a que funcionara: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Asegúrate de importar MatSortModule
  2. Especifica el matSort encabezado
  3. Asegúrese de envolver su fuente de datos en un MatTableDataSource
    • Esta es la que me ayudó a solucionar el problema (entiendes? Tipo que fuera). En la plantilla, me refería a la matriz directamente ( <table mat-table [dataSource]="this.products" matSort>) pero debería haber usado el objeto de fuente de datos que inicialicé en el código ( <table mat-table [dataSource]="this.dataSource" matSort>). La fuente de datos se inicializa comodataSource = new MatTableDataSource(this.products)
  4. Dígale a la fuente de datos sobre su tipo, en ngOnInit/ngAfterViewInit
  5. Escriba su propio tipo, si no desea utilizar MatTableDataSource
kumaheiyama
fuente
1

Si su tabla está dentro de un * ngIf y cree que tiene algo que ver con que no clasifique su tabla, entonces especificar su propia sortingDataAccessorfunción podría resolver el problema como lo hizo para mí. Tengo mi tabla dentro de un par de * ngIfs y sacarla de esos * ngIfs no tenía sentido:

`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
fuente
1

Una de las razones por las que MatSort podría no funcionar es cuando se agrega a una fuente de datos (es decir this.dataSource.sort = this.sort) antes de que se defina. Puede haber varias razones para esto:

  1. si agrega la ordenación en ngOnInit. En este punto, la plantilla aún no está renderizada, por lo que el MatSort con el que obtiene @ViewChild(MatSort, { static: true }) sort: MatSort;no está definido y, comprensiblemente, no hará nada. Una solución para este problema es pasar this.dataSource.sort = sorta ngAfterViewInit. Cuando se llama a ngAfterViewInit, su componente se representa y se debe definir MatSort.

  2. cuando usa * ngIf es su plantilla en el elemento de su tabla o una si son elementos principales y este * ngIf hace que su tabla no se muestre en el momento en que intenta establecer MatSort. Por ejemplo, si tiene *ngIf="dataSource.data.length > 0"en su elemento de tabla (para representarlo solo si hay datos presentes) y lo establece this.dataSource.sort = this.sortjusto después de establecer this.dataSource.datacon sus datos. La vista del componente no se volverá a generar todavía, por lo que MatSort seguirá sin estar definido.

Para que MatSort funcione y aún muestre condicionalmente su tabla, puede decidir reemplazar el *ngIfcon [hidden]como se indica en muchas otras respuestas. Sin embargo, si desea mantener su declaración * ngIf, puede usar la siguiente solución. Esta solución funciona para Angular 9, no la he probado en versiones anteriores, así que no estoy seguro de si funciona allí.

Encontré esta solución aquí: https://github.com/angular/components/issues/10205

En lugar de poner:

@ViewChild(MatSort) sort: MatSort;

use un setter para matSort. Este setter se activará una vez que matSort en su vista cambie (es decir, se defina la primera vez), no se activará cuando cambie su clasificación haciendo clic en las flechas. Esto se verá así:

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

Si tiene otras funciones que (programáticamente) cambian la clasificación, no estoy seguro de si se activará nuevamente, no lo he probado. Si desea asegurarse de que solo establece el tipo si el tipo no estaba definido, puede hacer algo como esto:

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

Vea si tiene algún error de JavaScript en la consola. Podría ser que alguna otra cosa fallara antes de que se inicializara la clasificación.

Ε Г И І И О
fuente