Демонстрація сортування матового столу не працює


107

Я намагаюся змусити 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

Відповіді:


198

Для всіх, хто може мати цю проблему: проблема полягала в тому, що я не прочитав посилання на 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
Augustas

2
Чи можу я сказати, що це SOB? Я витратив 1 годину, намагаючись зрозуміти, чому мій ViewChild не працює. Чи не можуть вони імпортувати / експортувати цей MatSortModule з MatTableModule ??
Сампгун

7
Я імпортував MatSortModuleі BrowserAnimationsModule, і я переконався, що значення matColumnDef відповідає імені властивості, але я все ще не можу змусити його щось робити.
Тревор

133

У мене була проблема з тим, що функція сортування працювала, але вона не правильно сортувала. Я зрозумів, що matColumnDefмає мати таку ж назву властивості моєї, на class / interfaceяку я посилаюся matCellDef.

Згідно Кутовий Матеріал документації :

За замовчуванням 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>

В директиві namein matColumnDefмає бути те саме, що і nameв <mat-cell>компоненті.


1
Що у своєму прикладі ви посилаєтесь? Для порівняння було б корисно переглянути і ваш інтерфейс.
isherwood

1
Я використовував "Id" як назву стовпця, тоді як сутність мала "id". Різниця у справі полягала в тому, що він не запускався (через помилку з рефакторингу). Тепер це вирішено. Дякую
NitinSingh

2
Дякую, це дуже корисно.
Бохао Л.І.

2
@NitinSingh, а що, якщо вам потрібно викликати функцію на element , наприклад, `{{row.getName ()}}`
кодовий запис

1
Я цілком винен тобі пивом, тому що я затримався на цій проблемі якийсь час, і цей коментар вирішив мою проблему.
ноель

99

Якщо таблиця знаходиться всередині * ngIf, вона не працюватиме. Це спрацює, якщо його змінити на [прихований]


33
!!! Врятуй мій день !!! Використовуйте замість<div *ngIf="xxx"> від<div [hidden]="!xxx">
Mark

1
Можна підтвердити, це спрацювало і у мене. Спасибі зерги!
clo5ure

1
Щиро дякую, це коштувало мені стільки часу !!
themightylc

1
Або просто встановити джерело даних у ngAfterViewInit замість ngOnInit
user3666653

1
Це найбільш "прихована" проблема, яка могла статися, дякую за рішення! документація могла попередити про це
Рейчерр,

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>

У моєму випадку oppNo однаковий для імені matColumnDef та імені * matCellDef, і сортування працює нормально.


Цікаво. Так було і для мене. Але, чи знаєте ви фактичні міркування за цим, чи це насправді якась "помилка"?
Таблиця повернення

22

Додавання сортування в блоці тайм-ауту працює для мене,

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

Якщо ви не хочете використовувати гачки lifecykle.


1
дурний хак, але це працює, будь-яка ідея, чому це не працює без тайм-ауту?
Рубен

Я занадто довго пробував усе інше, думаючи, що зійшов з розуму. Працював як шарм!
willpnw

4
Дійсно поганий спосіб зробити це. Це працює, тому що ви дозволяєте пройти деякий час після ініціалізації компонента, щоб побудувати dataSource, а потім ви додасте сортування та пагінатор. Найкраще - перемістити будівництво datSource у ngOnInit, а потім перемістити призначення сортування та створення пагінатора в AfterViewInit. Саме для цього існують гачки життєвого циклу.
Selam Гетачеу

20

Я також потрапив у цю проблему. Оскільки вам потрібно почекати, поки дитина буде визначена, вам доведеться впроваджувати та використовувати AfterViewInit, а не onInit.

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

Дивовижно! Дякую
Шашанк Вівек

Я використовую таблицю з сортуванням, фільтрацією та пагінацією. Чи знаєте ви, чому саме сортування має бути визначено ngAfterViewInit? Решта працювала з ngOnInit. Просто щоб спробувати зрозуміти, це виправлено завдяки вам
Ніколас М.

14

Я витратив години на це питання. Ознайомившись із низкою тем, ось кроки, які я зробив.

  1. Як згадував @avern , вам потрібно імпортуватиMatSortModule .
  2. Переконайтеся, що ви НЕ вкладаєте таблицю в *ngIf. Змініть його , [hidden]як @zerg рекомендується . (Я не розумію, чому)

Сподіваюся, це допомагає.


Він витратив свій день, щоб з'ясувати проблему, і дурний не виявляє жодної помилки.
surekha shelake

11

Моїм рішенням було виправити кілька речей (в основному об’єднавши більшість рішень на цій сторінці).

Що потрібно перевірити:

  1. BrowserModule, MatTableModule, MatSortModule Модулі слід імпортувати у кореневий файл модулів.
  2. Переконайтеся, що використовували MatTableDatasource клас, і передайте в нього масив даних як параметр
  3. Переконайтеся, що ваша таблиця не вкладена в *ngIf=....директиву. Замість цього використовуйте інші умовні операції (все ще не розумієте, чому).

3

Для мене спрацювала заміна * ngIf на атрибут [hidden] для тегу mat-table. Як опублікувати це як помилку в спільноті 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 може не працювати, є те, коли він додається до джерела даних (тобто this.dataSource.sort = this.sort) до його визначення. Причин цього може бути декілька:

  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. Можливо, якась інша річ не вдалася до ініціалізації сортування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.