매트 테이블 정렬 데모가 작동하지 않음


108

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)없이 나를 위해 일하고 있습니다. sort : MatSort; 어떤 이유 ?
user123456

답변:


201

이 문제가있을 수있는 다른 사람을 위해 : 문제는 내가 MatSortModule을 가져와야한다고 말한 부분 인 앵귤러 머티리얼 웹 사이트에서 API 참조를 제대로 읽지 못했다는 것입니다. app.module.ts의 가져 오기 목록을 다음 으로 변경 한 후

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

잘 작동했다


45
문서에이 모듈에 대한 언급이 없습니다. material.angular.io/components/table/overview#sorting 이것도 한 시간을 낭비했습니다.
Sonic Soul

8
헤더 텍스트를 클릭 할 수 있고 아이콘도 있지만 여전히 정렬이 작동하지 않습니다.
SPnL

3
BrowserAnimationsModuleapp.module.ts에서도 가져 왔는지 확인하십시오
Augustas

2
SOB라고 말할 수 있습니까? ViewChild가 작동하지 않는 이유를 파악하기 위해 1 시간을 보냈습니다. MatTableModule에서이 MatSortModule을 가져 오거나 내보낼 수 없습니까 ??
Sampgun

7
MatSortModule및을 가져 BrowserAnimationsModule왔고 matColumnDef 값이 속성 이름과 일치하는지 확인했지만 여전히 아무것도 할 수 없습니다.
Trevor

136

정렬 기능이 작동하지만 제대로 정렬되지 않는 문제가있었습니다. 나는 그것이 내가 참조하고있는 matColumnDef나의 재산과 같은 이름을 가져야 한다는 것을 깨달았다 .class / interfacematCellDef

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

1
열 이름으로 "Id"를 사용했지만 엔티티에는 "id"가 있습니다. 케이스 차이는 (리팩토링 미스로 인해) 실행되지 않게 만드는 것입니다. 이제 해결되었습니다. 감사합니다
NitinSingh

2
감사합니다. 매우 유용합니다.
Bohao LI

2
@NitinSingh, 다음 element과 같이 에서 함수를 호출해야하는 경우 `{{row.getName ()}}`
codentary

2
나는이 문제에 한동안 붙어 있었고이 의견이 내 문제를 해결했기 때문에 나는 당신에게 맥주를 전적으로 빚지고 있습니다.
noel

101

테이블이 * ngIf 안에 있으면 작동하지 않습니다. [숨김]으로 변경하면 작동합니다.


34
!!! 당신은 나의 하루를 구하십시오 !!! 대신 사용<div *ngIf="xxx"><div [hidden]="!xxx">
마크

1
확인할 수 있습니다. 이것은 저에게도 효과적이었습니다. 고마워 저그!
clo5ure 19

1
정말 감사합니다. 시간이 많이 걸렸습니다 !!
themightylc

1
또는 ngOnInit 대신 ngAfterViewInit에서 데이터 소스를 설정하십시오
user3666653

1
이것은 발생할 수있는 가장 "숨겨진"문제입니다. 솔루션에 감사드립니다! 문서는 이것에 대해 경고 할 수있었습니다
Raycherr

36

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>

제 경우에는 oppNo가 matColumnDef 이름과 * matCellDef 이름에 대해 동일하며 정렬이 잘 작동합니다.


흥미 롭군. 나에게도 그랬다. 그러나, 이것 뒤에있는 실제적인 이유를 알고 있습니까, 아니면 실제로 일종의 "버그"일까요?
ReturnTable 2019-08-22

22

시간 초과 블록 내에서 정렬을 추가하면 효과가 있습니다.

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

lifecykle hooks를 사용하고 싶지 않은 경우.


1
어리석은 해킹이지만 작동하지만 시간 초과없이 작동하지 않는 이유는 무엇입니까?
Ruben

나는 내가 미쳐 가고 있다고 생각하면서 다른 모든 것을 시도하는 데 너무 오래 걸렸다. 매력처럼 일했다!
willpnw

4
정말 나쁜 방법입니다. 구성 요소 초기화 후 dataSource를 빌드 한 다음 정렬 및 페이지 지정자를 추가하기 때문에 시간이 조금 지나면 작동합니다. 가장 좋은 방법은 ngOnInit에서 datSource buidling을 이동 한 다음 AfterViewInit에서 정렬 및 페이지 지정자 지정을 이동하는 것입니다. 이것이 Lifecycle Hooks가 존재하는 이유입니다.
Selam Getachew

21

나는 또한이 문제를 쳤다. 자식이 정의 될 때까지 기다려야하므로 AfterViewInitonInit가 아닌 을 구현하고 사용해야 합니다.

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

대박 ! 감사합니다
Shashank Vivek

정렬, 필터링 및 페이지 매김이있는 테이블을 사용하고 있습니다. 정렬 만 정의해야하는 이유가 ngAfterViewInit무엇입니까? 나머지는 ngOnInit. 이해하려고 노력하는 것뿐입니다. 덕분에 고쳐졌습니다
Nicolas M.

14

나는이 문제에 몇 시간을 보냈다. 여러 스레드를 읽은 후 수행 한 단계는 다음과 같습니다.

  1. 으로 @avern 언급 , 당신은 가져와야합니다 MatSortModule.
  2. 당신이되어 있는지 확인 NOT A의 테이블을 둘러싸 *ngIf. @zerg 권장[hidden] 대로 변경하십시오 . (이유를 모르겠습니다)

도움이 되었기를 바랍니다.


문제를 찾기 위해 하루를 낭비했으며 바보는 오류를 표시하지 않습니다.
surekha shelake

11

내 해결책은 몇 가지를 수정하는 것이 었습니다 (기본적으로이 페이지에있는 대부분의 솔루션을 병합).

확인해야 할 사항 :

  1. BrowserModule, MatTableModule, MatSortModule 모듈은 루트 모듈 파일로 가져와야합니다.
  2. MatTableDatasource클래스를 사용 하고 데이터 배열을 매개 변수로 전달 했는지 확인하십시오.
  3. 테이블이 *ngIf=....지시문에 중첩되지 않았는지 확인하십시오 . 대신 다른 조건부 연산을 사용하십시오 (여전히 이유를 이해하지 못함).

3

나를 위해 * ngIf를 매트 테이블 태그의 [숨김] 속성으로 바꾸면 작동했습니다. 이것을 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

저에게는 두 가지 문제가있었습니다.

  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는 정의되기 전에 dataSource (예 :)에 추가되는 경우입니다. 이에 대한 여러 가지 이유가있을 수 있습니다.

  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를 사용하십시오. 이 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

콘솔에 자바 스크립트 오류가 있는지 확인하세요. 정렬이 초기화되기 전에 다른 일이 실패했을 수 있습니다.


0

실제로 matColumnDef 이름 (즉, 열 이름)과 클래스 / 인터페이스 속성 이름이 작동하려면 동일해야합니다.

클래스 / 인터페이스 속성 이름을 변경할 수없는 경우도 있습니다.이 경우 아래와 같이 사용자 지정 정렬을 구현할 수 있습니다.

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];
        }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.