垫子表排序演示不起作用


105

我正在尝试使mat-table排序在本地工作,尽管我可以使数据按预期显示,但是单击标题行并不会像在在线示例中那样进行排序(什么都没有发生)。我正在尝试使此演示在本地运行:https : //material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

我已经使用Angular CLI生成了一个新项目,然后执行以下步骤:https : //material.angular.io/guide/getting-started

这是我的本地文件:

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

有谁知道为什么它会像在线表格一样显示但缺少排序功能?


我会先调试应用程序。有什么错误吗?运行ng test --sm=false,看看结果如何。
k.vincent

它对我来说没有@ViewChild(MatSort)排序:MatSort; 任何原因 ?
user123456

Answers:


197

对于其他任何可能遇到此问题的人:问题是我在角材料网站上没有正确阅读API参考,该部分说我必须导入MatSortModule。在我将app.module.ts中的导入列表更改

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

工作正常


45
在文档中没有提及此模块。 material.angular.io/components/table/overview#sorting 我也为此浪费了一个小时。
Sonic Soul

8
很好,在标题文本中可单击,图标也在那里,但仍然无法排序。
SPnL

2
检查是否BrowserAnimationsModule也导入了app.module.ts
奥古斯塔斯

2
我可以说它们是SOB吗?我花了1个小时来弄清楚为什么ViewChild无法正常工作。他们不能从MatTableModule导入/导出此MatSortModule吗?
Sampgun

7
我已经导入了MatSortModuleBrowserAnimationsModule,并且已经确保matColumnDef值与属性名称匹配,但是我仍然无法使它做任何事情。
Trevor

131

我有一个问题,排序功能可以正常工作,但是排序不正确。我意识到必须matColumnDef具有与class / interface我所引用的属性相同的名称matCellDef

根据Angular Material 文档

默认情况下,MatTableDataSource进行排序时假设已排序的列名称与该列显示的数据属性名称匹配。

举个例子:

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

namematColumnDef指令必须是一样的name在所使用的<mat-cell>组件。


1
您在示例中引用的是什么?同时,也可以查看您的界面以进行比较。
isherwood '18

1
我使用“ Id”作为列名,而实体使用“ id”。区分大小写的原因是它没有运行(由于重构遗漏)。现在解决了。谢谢
NitinSingh '18

2
谢谢,这非常有用。
李伯豪

2
@NitinSingh,如果您需要在上调用函数element,例如`{{row.getName()}}`
密码为

1
我完全欠你一杯啤酒,因为我已经在这个问题上停留了一段时间,而此评论解决了我的问题。
noel

99

如果表位于* ngIf内,则它将无法正常工作。如果将其更改为[hidden],它将起作用


33
!!!您保存我的一天!使用替代<div *ngIf="xxx"><div [hidden]="!xxx">
马克

1
可以确认,这也对我有用。谢谢虫族!
clo5ure

1
非常感谢,这花了我很多时间!
themightylc

1
或者只是设置ngAfterViewInit代替ngOnInit数据源
user3666653

1
这是最有可能发生的“隐藏”问题,感谢您的解决方案!文档可能对此有所警告
Raycherr

35

matColumnDef名称和* matCellDef实际值名称应相同

例:

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

在我的情况下,opColumn与matColumnDef名称和* matCellDef名称相同,并且排序正常。


有趣。我也是这种情况。但是,您是否知道背后的实际原因,或者实际上是某种“错误”?
ReturnTable '19

22

在超时块中添加排序对我来说很有效,

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

如果您不想使用救生圈钩。


1
愚蠢的骇客,但它能起作用,不知道为什么没有超时就无法起作用吗?
鲁本

我花了太多时间尝试其他所有事情,以为自己发疯了。像魅力一样工作!
willpnw

4
确实是一种不好的方法。之所以有效,是因为在组件初始化之后要花一些时间来构建dataSource,然后添加sort和paginator。最好的方法是在ngOnInit中移动datSource,然后在AfterViewInit中移动sort和paginator分配。这就是生命周期挂钩的用途。
Selam Getachew

20

我也遇到了这个问题。由于需要等待定义子项,因此必须实现和使用AfterViewInit,而不是onInit。

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

太棒了!谢谢
Shashank Vivek

我正在使用带有排序,过滤和分页的表格。您有什么线索为什么只需要在其中定义排序ngAfterViewInit?其余的从ngOnInit。只是为了理解,已感谢您修复
Nicolas M.

14

我在这个问题上花了几个小时。在阅读了多个线程之后,这里是我执行的步骤。

  1. @avern所述,您需要导入MatSortModule
  2. 确保您没有将表格包含在中*ngIf。将其更改[hidden]@zerg建议。(我不明白为什么)

希望这可以帮助。


它浪费了我的时间来找出问题,而且愚蠢的程序没有显示任何错误。
surekha shelake

11

我的解决方案是修复一些问题(基本上合并此页面中的大多数解决方案)。

检查事项:

  1. BrowserModule, MatTableModule, MatSortModule 模块应导入到根模块文件中。
  2. 确保使用过MatTableDatasource类并在其中传递数据数组作为参数
  3. 确保您的表未嵌套在*ngIf=....指令中。请改用其他条件操作(仍然不明白为什么)。


3

我通过使用与* matColumnDef相同的名称命名表数据来解决此问题,例如:

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

代替

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

我有2个问题。

  1. matColumnDef和matCellDef->名称不同
  2. 我正在从服务中获取数据。ngOnInit排序不起作用。替换为

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


2

我发现了这个老博客,这有助于我使其正常工作:https : //www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. 确保导入 MatSortModule
  2. 指定matSort标题
  3. 确保将数据源包装在 MatTableDataSource
    • 这是帮助我进行分类(得到吗?进行分类)的工具。在模板中,我直接引用了数组(<table mat-table [dataSource]="this.products" matSort>),但我应该使用在代码(<table mat-table [dataSource]="this.dataSource" matSort>)中初始化的数据源对象。数据源初始化像dataSource = new MatTableDataSource(this.products)
  4. ngOnInit/中告知数据源您的排序ngAfterViewInit
  5. 如果您不想使用自己的排序 MatTableDataSource

1

如果您的表位于* ngIf内,并且您认为它与不对表进行排序有关,那么指定自己的sortingDataAccessor函数可能会像对我一样解决此问题。我将表放在几个* ngIfs中,并将它们从这些* ngIfs中取出是没有道理的:

`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

MatSort可能无法工作的原因之一是在this.dataSource.sort = this.sort定义之前将其添加到数据源(即)中。可能有多种原因:

  1. 如果您在ngOnInit中添加排序。此时模板尚未呈现,因此您获得的MatSort @ViewChild(MatSort, { static: true }) sort: MatSort;是未定义的,可以理解将不会执行任何操作。解决此问题的方法是移至this.dataSource.sort = sortngAfterViewInit。调用ngAfterViewInit时,将渲染您的组件,并应定义MatSort。

  2. 当您使用* ngIf是表格元素的模板时,如果模板是其父元素,则为* ngIf,这导致您在尝试设置MatSort时无法渲染表格。例如,如果您*ngIf="dataSource.data.length > 0"在表元素上具有(仅在存在数据的情况下才呈现它),并且在设置数据this.dataSource.sort = this.sort之后立即进行设置this.dataSource.data。组件视图将不会重新渲染,因此MatSort仍将是未定义的。

为了获得MatSort工作,仍然有条件地显示你的表,你可以决定更换*ngIf[hidden]在多个其他的答案说。但是,如果要保留* ngIf语句,则可以使用以下解决方案。该解决方案适用于Angular 9,我尚未在以前的版本中对其进行过测试,因此我不确定它是否可以在该版本中使用。

我在这里找到了这个解决方案:https : //github.com/angular/components/issues/10205

而不是:

@ViewChild(MatSort) sort: MatSort;

为matSort使用setter。一旦您的视图中的matSort发生更改(即首次定义),此设置器就会触发,当您通过单击箭头更改排序时,该设置器将不会触发。看起来像这样:

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

如果您还有其他功能(以编程方式)更改了排序功能,则不确定是否会再次触发排序,我尚未对此进行测试。如果您不想确保仅在未定义排序时设置排序,则可以执行以下操作:

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

0

查看控制台中是否存在任何JavaScript错误。可能是其他事情在初始化排序之前失败了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.