Mat-table排序演示不起作用

时间:2017-10-23 15:33:05

标签: angular angular-material

我试图让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>

表格排序-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 -->

表格排序-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 */

有没有人知道为什么它会像在线表一样显示但缺乏排序功能?

25 个答案:

答案 0 :(得分:141)

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

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

它工作正常

答案 1 :(得分:58)

我遇到的问题是排序功能正在运行,但它没有正确排序。我意识到matColumnDef必须与我在class / interface中引用的matCellDef的属性具有相同的名称。

根据Angular Material documentation

  

默认情况下,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>

name指令中的matColumnDef必须与name组件中使用的<mat-cell>相同。

答案 2 :(得分:16)

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

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

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

答案 3 :(得分:15)

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名称相同,并且排序正常。

答案 4 :(得分:14)

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

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

希望这会有所帮助。

答案 5 :(得分:7)

我的解决方案是修复几处问题(基本上将本页中的大多数解决方案合并)。

要检查的内容:

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

答案 6 :(得分:5)

对我来说有2个问题。

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

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

答案 7 :(得分:5)

在您的 app.module.ts 中,执行以下操作:

导入

import { MatSortModule } from '@angular/material/sort';

然后添加

imports: [
    ...
    MatSortModule
],

答案 8 :(得分:4)

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

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

答案 9 :(得分:4)

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

,它将起作用

答案 10 :(得分:4)

我找到了这个旧博客,该博客帮助我使其正常工作: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. 请确保导入MatSortModule
  2. 指定matSort标头
  3. 确保将数据源包装在MatTableDataSource
    • 这是帮助我解决问题的工具(明白吗? sort )。在模板中,我直接引用了数组(<table mat-table [dataSource]="this.products" matSort>),但是我应该使用在代码(<table mat-table [dataSource]="this.dataSource" matSort>)中初始化的数据源对象。数据源的初始化类似于dataSource = new MatTableDataSource(this.products)
  4. ngOnInit / ngAfterViewInit
  5. 告诉您有关排序的数据源
  6. 如果不想使用MatTableDataSource
  7. ,请自行编写排序

答案 11 :(得分:4)

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

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

  2. 当您使用* ngIf是表元素上的模板,或者使用* 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;
    }
}

答案 12 :(得分:2)

对于那些对这些命名必须相同感到困惑的人,我做了一些测试:

这将起作用(属性名称与列def相同):

<ng-container matColumnDef="version">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version']

这将不起作用(属性名称与列def不同):

<ng-container matColumnDef="version2">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version2']

F,这也不起作用(属性的长度):

<ng-container matColumnDef="length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['length']

这也不是:

<ng-container matColumnDef="ids.length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['ids.length']

答案 13 :(得分:1)

在我的场景中,我通过将表数据命名为与* 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>

答案 14 :(得分:1)

我找到了这个问题的多个答案,但是单独实施它们并没有给我任何结果。因此,我尝试合并答案,并且有效。

首先,我在NgAfterViewInit接口内添加了ViewChild排序。 (最初是在通过NgOnInit

调用的函数内部
 ngAfterViewInit(){
    this.tableData.sort = this.sort;
  }

第二步,我将容器内的* ngIf更改为[hidden]。我确实收到一条错误消息,指出未加载该值。但是到目前为止,这并不是主要的问题。

之前

<div class="mat-elevation-z0 container-fluid" *ngIf={some boolean resultant condition}>

之后

<div class="mat-elevation-z0 container-fluid" [hidden] = {negation of your boolean expression}>

psst .. 您还可以考虑在通过以下方式加载表格时添加加载微调器 垫脚到上述bs上方。

    <ng-container matColumnDef="loading">
                    <mat-footer-cell *matFooterCellDef colspan=6>
                        <div class="uploader-status">
                            <mat-spinner strokeWidth="25" [diameter]="100" title="Server Starting" ></mat-spinner>
                        </div>
                    </mat-footer-cell>
</ng-container>


<mat-footer-row *matFooterRowDef="['loading']" [ngStyle]="{'display': (this.candidateService.candidateRecords!=null) ? 'none':'block'}"></mat-footer-row>

答案 15 :(得分:1)

如果您在此处阅读所有答案,但没有任何帮助,也许您遇到了与我相同的问题。

问题是我的MatTableDataSource对象

dataSource = new MatTableDataSource<StbElement>(ELEMENT_DATA);

在不带this的html文件中使用。

更改:

<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">

收件人:

<table mat-table [dataSource]="this.dataSource" matSort class="mat-elevation-z8">

解决了问题。

答案 16 :(得分:1)

对我来说,用[hidden]属性将* ngIf替换为mat-table标签有效。 如何将其作为错误发布到Angular Material社区?

答案 17 :(得分:0)

实际上,matColumnDef名称(即列名称)和您的Class / Interface属性名称应该相等才能使其正常工作。

有时我们无法更改我们的Class / Interface属性名称,在这种情况下,我们可以如下实现自定义排序。

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

如果我们在'id'列上进行排序,它将无法正常工作。 尝试使用自定义排序

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];
        }
}

答案 18 :(得分:0)

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

sortingDataAccessor

答案 19 :(得分:0)

除了前面所有的答案,有时在数据检索时表格是不可见的。例如,我必须在模态/对话框中显示带有 MatSort 和 MatPaginator 的表格。因此,我必须通过它们各自的输出发射器函数传入元素,如下所示:

<... matSort #sort="matSort" (matSortChange)="sortData(sort)">
<... #paginator (page)="changePaginator(paginator)">

在打字稿中:

  @ViewChild(MatSort, { static: false }) set sort(s: MatSort) {
      this.dataSource.sort = s;
  }
  @ViewChild(MatPaginator, { static: false }) set paginator(p: MatPaginator) {
      this.dataSource.paginator = p;
  }

  sortData(sort: MatSort) {
      this.sort = sort;
  }
  changePaginator(paginator: MatPaginator) {
      this.paginator = paginator;
  }

确保在其输入指令中设置分页器的默认值,因为上面的代码将在分页后设置元素,即:[pageSize]="5" [length]="dataSource?.data?.length"。请将此作为之前所有解决方案的最后手段。

答案 20 :(得分:0)

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

答案 21 :(得分:0)

此问题主要发生在 sortdataSource 之前初始化时。在演示中发现 here dataSource 是静态初始化的,因此不会发生任何问题。但是,当您必须异步获取数据时,您需要等待来自 API 调用的响应到达并分配给 dataSource,然后再初始化 sort 实例变量。

答案 22 :(得分:0)

我不知道原因;但是将 this.dataSource.sort = this.sort; 赋值给 ngAfterViewInit() 方法不起作用。即使我确认此功能在页面加载后仍然无法正常工作。 我的解决方案是将排序分配放在 ngOnInit() 方法中。

 ngOnInit(): void {
   this.service.getAllAudits().subscribe(result => { 
    this.dataSource  = new MatTableDataSource(result); 
    this.dataSource.sort = this.sort; 
  });

}

答案 23 :(得分:0)

下面的代码非常适合我,

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

答案 24 :(得分:-1)

My solution for this problem is as below - 


1. These two lines will go in the same order.

    this.dataSource = new MatTableDataSource(myRowDataArray);// this dataSource is used in table tag.
    this.dataSource.sort = this.sort;


2. Pass MatTableDataSource object in [dataSource] 
    <table mat-table [dataSource]="dataSource">
    // rest of the table definition here
    </table>

3. By default, the MatTableDataSource sorts with the assumption that the sorted column's name matches the data property name that the column displays.

Example - 
    <ng-container matColumnDef="date" >
          <th class="headers" mat-header-cell  *matHeaderCellDef mat-sort-header>Date</th>
          <td class="data" mat-cell *matCellDef="let row">{{row.date|date}}</td>
    </ng-container>

4. If the table is inside *ngIf,then replace it with [hidden] or some other filter.

我错过了第二点。

Cheers!