マットテーブルの並べ替えのデモが機能しない


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

@ViewChild(MatSort)ソートなしで私のために機能しています:MatSort; 何らかの理由 ?
user123456

回答:


196

この問題を抱えている可能性のある人のために:問題は、アンギュラーマテリアルのWebサイトでAPIリファレンスを正しく読んでいないことでした。app.module.tsのインポートリストを

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

それはうまくいきました


44
ドキュメントではこのモジュールについての言及はありません。 material.angular.io/components/table/overview#sorting これについても1時間無駄になりました。
Sonic Soul

8
これは問題ありません。ヘッダーのテキストはクリック可能で、アイコンもそこにありますが、ソートは機能しません。
SPnL 2018年

2
BrowserAnimationsModuleがapp.module.tsにもインポートされているかどうかを確認する
Augustas

2
それらはSOBであると言えますか?1時間かけて、ViewChildが機能しなかった理由を理解しようとしました。彼らはこのMatSortModuleをMatTableModuleからインポート/エクスポートできませんか?
Sampgun

6
私は、インポートしたMatSortModuleBrowserAnimationsModule、私はmatColumnDef値は、プロパティ名と一致することを確実にしました、まだ私はまだそれが何かをするために取得することができませんでしだ。
Trevor

131

並べ替え機能が動作しているのに問題がありましたが、正しく並べ替えられませんでした。私はで参照しmatColumnDefclass / 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 2018年

1
エンティティが「id」を持っているのに対し、私は列の名前として「Id」を使用していました。ケースの違いは、それを実行しないようにすることでした(リファクタリングのミスのため)。ありがとう
NitinSingh 2018

2
ありがとう、とても便利です。
Bohao LI、

2
あなたは上の関数を呼び出す必要があれば何@NitinSingh、elementこの`{{row.getName()}}`のように、
codentary

1
しばらくこの問題に悩まされていて、このコメントで問題が解決したので、私はあなたにビールを借りています。
noel

99

テーブルが* ngIf内にある場合、機能しません。[非表示]に変更すれば機能します


33
!!!あなたは私の日を救う!!! 代わりに使用してください<div *ngIf="xxx"><div [hidden]="!xxx">
マーク・

1
確認できます。これも私にとってはうまくいきました。ありがとうzerg!
clo5ure

1
どうもありがとうございました。
themightylc

1
または、ngOnInitの代わりにngAfterViewInitでデータソースを設定するだけ
-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

22

タイムアウトブロック内に並べ替えを追加すると、

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

ライフサイクルフックを使用したくない場合。


1
愚かなハックが動作しますが、タイムアウトなしでは動作しない理由はありますか?
Ruben

私は他のすべてを試すのにあまりにも長い時間を費やし、私は狂っていたと思いました。魅力のように働いた!
willpnw 2019

4
本当に悪いやり方です。これは、コンポーネントの初期化後、dataSourceが構築されるようにしばらく経過させてから、並べ替えとページネーターを追加することで機能します。最善の方法は、ngOnInitでdatSource構築を移動してから、AfterViewInitでソートとページ編集の割り当てを移動することです。これがライフサイクルフックの目的です。
Selam Getachew

20

私もこの問題に遭遇しました。子が定義されるのを待つ必要があるため、AfterViewInitonInitではなくを実装して使用する必要があります。

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

驚くばかり !ありがとう
Shashank Vivek

私は、並べ替え、フィルタリング、およびページネーションを備えたテーブルを使用しています。ソートのみを定義する必要がある理由はありngAfterViewInitますか?残りはから働いていましたngOnInit。それはただ理解しようとすることです、それはあなたのおかげで修正されました
ニコラス・M.

14

私はこの問題に何時間も費やしました。いくつかのスレッドを読んだ後、ここに私がやったステップがあります。

  1. 以下のよう@avernが言及した、あなたは、インポートする必要がありますMatSortModule
  2. テーブルをで囲まないようにしてください*ngIf@zergが推奨する[hidden]ように変更します。(理由はわかりません)

お役に立てれば。


それは問題を見つけるために私の日を無駄にし、愚か者はエラーを示していません。
シュレハシェレイク

11

私の解決策はいくつかのことを修正することでした(基本的にこのページのほとんどの解決策をマージします)。

確認すること:

  1. BrowserModule, MatTableModule, MatSortModule モジュールはルートモジュールファイルにインポートする必要があります。
  2. MatTableDatasourceクラスを使用し、データ配列をパラメータとして渡すようにしてください
  3. テーブルが*ngIf=....ディレクティブにネストされていないことを確認してください。代わりに他の条件付き演算を使用してください(まだ理由がわかりません)。

3

私にとっては、マットテーブルタグの* ngIfを[hidden]属性に置き換えることができました。これをバグとしてAngular Materialコミュニティに投稿するにはどうすればよいですか?


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が機能しない理由の1つは、MatSort this.dataSource.sort = this.sortが定義される前にdataSource(つまり)に追加された場合です。これには複数の理由が考えられます。

  1. ngOnInitで並べ替えを追加した場合。この時点ではテンプレートはまだレンダリングされていないため、取得したMatSort @ViewChild(MatSort, { static: true }) sort: MatSort;は定義されておらず、当然何も実行されません。この問題の解決策はthis.dataSource.sort = sort、ngAfterViewInit に移動することです。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のセッターを使用します。このセッターは、ビューの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エラーがあるかどうかを確認します。ソートが初期化される前に、他の何かが失敗した可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.