Демонстрация сортировки мат-таблицы не работает
Я пытаюсь получить 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 */
У кого-нибудь есть представление о том, почему он будет отображаться как онлайн-таблица, но ему не хватает функции сортировки?
34 ответа
Для всех, у кого может быть эта проблема: проблема заключалась в том, что я не прочитал ссылку на API должным образом на веб-сайте угловых материалов, ту часть, в которой говорилось, что я должен импортировать MatSortModule. После того, как я изменил свой список импорта в app.module.ts на
imports: [
BrowserModule,
MatTableModule,
MatSortModule
],
это работало нормально
У меня была проблема с тем, что функция сортировки работала, но она не сортировалась должным образом. Я понял, что 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>
name
в matColumnDef
Директива должна быть такой же, как name
используется в <mat-cell>
составная часть.
Если таблица находится внутри *ngIf, она не будет работать. Это будет работать, если оно будет изменено на [скрыто]
Одна из причин, по которой MatSort может не работать, - это когда он добавляется в источник данных (т.е. this.dataSource.sort = this.sort
) до его определения. Для этого может быть несколько причин:
если вы добавите сортировку в ngOnInit. На данный момент шаблон еще не отрисован, поэтому MatSort вы получаете с
@ViewChild(MatSort, { static: true }) sort: MatSort;
undefined и по понятным причинам ничего не сделает. Решение этой проблемы - переместитьthis.dataSource.sort = sort
в ngAfterViewInit. Когда вызывается ngAfterViewInit, ваш компонент отображается, и должен быть определен MatSort.когда вы используете *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;
}
}
Имя 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, и сортировка работает нормально.
Я также ударил эту проблему. Поскольку вам нужно подождать, пока ребенок определится, вы должны реализовать и использовать AfterViewInit
, а не onInit.
ngAfterViewInit (){
this.dataSource.sort = this.sort;
}
Добавление сортировки в блоке тайм-аута работает для меня,
dataSource = new MatTableDataSource(this.articleService.getAllArticles());
setTimeout(() => {
this.tableDataSource.sort = this.sort;
this.tableDataSource.paginator = this.paginator;
});
Я потратил часы на эту проблему. Прочитав несколько тем, вот шаги, которые я сделал.
- Как упоминалось в @avern, вам необходимо импортировать
MatSortModule
. - Убедитесь, что вы НЕ заключаете таблицу в
*ngIf
. Измените это на[hidden]
как рекомендовано @zerg. (Я не понимаю почему)
Надеюсь это поможет.
Мое решение состояло в том, чтобы исправить несколько вещей (в основном объединить большинство решений на этой странице).
Что нужно проверить:
BrowserModule, MatTableModule, MatSortModule
Модули следует импортировать в файл корневых модулей.- Убедитесь, что вы использовали
MatTableDatasource
class и передайте в него свой массив данных в качестве параметра - Убедитесь, что ваша таблица не вложена в
*ngIf=....
директива. Вместо этого используйте другие условные операции (все еще не понимаю, почему).
в вашем app.module.ts выполните ff:
Импортировать
import { MatSortModule } from '@angular/material/sort';
затем добавьте
imports: [
...
MatSortModule
],
Я нашел этот старый блог, который помог мне заставить его работать:https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort
- Обязательно импортируйте
MatSortModule
- Укажите
matSort
заголовок - Убедитесь, что ваш источник данных заключен в
MatTableDataSource
- Это тот, который помог мне разобраться (понять? Разобраться). В шаблоне я имел в виду массив напрямую (
<table mat-table [dataSource]="this.products" matSort>
), но мне следовало использовать объект источника данных, который я инициализировал в коде (<table mat-table [dataSource]="this.dataSource" matSort>
). Источник данных инициализируется какdataSource = new MatTableDataSource(this.products)
- Это тот, который помог мне разобраться (понять? Разобраться). В шаблоне я имел в виду массив напрямую (
- Сообщите источнику данных о своей сортировке в
ngOnInit
/ngAfterViewInit
- Напишите свой собственный сорт, если не хотите использовать
MatTableDataSource
Потратив на это время в течение нескольких недель. Я узнал вам следующее
- Вам нужно импортировать MatSortModule в ваш app.module.ts.
imports: [ ... MatSortModule ],
<ng-container matColumnDef="ledgerTransactionCreditSum"> <th mat-header-cell *matHeaderCellDef mat-sort-header> Ledger Transaction Credit </th> <td mat-cell *matCellDef="let element"> {{element.ledgerTransactionCreditSum}} </td> </ng-container> matColumnDef and element.ledgerTransactionCreditSum variable and matcolumn def shoulde be same
- Определите сортировку и пагинатор в ngViewInit
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.dataSource.paginator?._changePageSize(400)
}
Я исправил это в своем сценарии, назвав данные таблицы с тем же именем, что и *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>
Основные причины, по которым mat-sort и mat-paginator не работают:
- Модули MatSortModule и MatPaginatorModule не импортируются
- Таблица находится под условием *ngIf
- matColumnDef должен быть таким же, как matCellDef и массив displayColumns.
У меня было 2 проблемы.
- Имена matColumnDef и matCellDef -> разные
Я получал данные из службы. Сортировка ngOnInit не работала. Заменено на
ngAfterViewInit() { this.dataSource.sort = this.sort; }
Для всех, кто не понимает, что эти наименования должны быть одинаковыми, я провел небольшое тестирование:
Это будет работать (имя свойства такое же, как у столбца 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']
Fyi, это тоже НЕ работает (длина свойства):
<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']
Приведенный ниже код отлично работал у меня,
@ViewChild(MatSort) set matSort(sort: MatSort) {
if (!this.dataSource.sort) {this.dataSource.sort = sort;}}</pre>
Для меня сработала замена *ngIf атрибутом [hidden] для тега mat-table. Как опубликовать это как ошибку в сообществе Angular Material?
Я нашел несколько ответов на этот вопрос, но их индивидуальная реализация не дала никаких результатов. Поэтому я попытался объединить ответы, и это сработало.
Во-первых, я добавил сортировку ViewChild внутри интерфейса NgAfterViewInit. (Изначально это было внутри функции, которая вызывалась через NgOnInit
ngAfterViewInit(){
this.tableData.sort = this.sort;
}
На втором этапе я изменил *ngIf внутри контейнера на [скрытый]. Я получаю сообщение об ошибке, в котором говорится, что значение не загружено. Но до сих пор это не главная проблема.
до
<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>
Если ваша таблица находится внутри *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;
}
}`
У меня работает..
@ViewChild('sortConfigTable', { static: false }) sortConfigTable: MatSort;
после первоначального назначения данных
this.dataSource.data = ds;
setTimeout(() => {
if (this.sortConfigTable) {
this.dataSource.sort = this.sortConfigTable;
}
}, 1000);
Если вы прочитали все ответы до этого момента и ничего не помогло, возможно, у вас та же проблема, что и у меня.
Проблема была в том, что мой
MatTableDataSource
объект
dataSource = new MatTableDataSource<StbElement>(ELEMENT_DATA);
Использовался в html файле без
this
.
Меняется:
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
Кому:
<table mat-table [dataSource]="this.dataSource" matSort class="mat-elevation-z8">
Исправлена проблема.
В дополнение ко всем предыдущим ответам, иногда таблица не отображается во время извлечения данных. Например, мне пришлось отображать таблицу с 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"
. Пожалуйста, используйте это как последнее средство для всех предыдущих решений.
Брат, смотри это видео. Сейчас это работает для меня. Имя отображаемого столбца должно совпадать с именем поля, подлежащего сортировке. Пример:
https://www.youtube.com/watch?v=Rpem1hN-WrU&amp;ab_channel=KnowledgeBase
модель
export interface Ticket {
order: number;
}
отображаемые столбцы
public displayedColumns: string[] = ['order'];
HTML
<ng-container matColumnDef="order">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Pedido</th>
<td mat-cell *matCellDef="let element" data-label="order">{{element.order}}</td>
</ng-container>
изменение
@ViewChild('matsort') sort: MatSort;
к
@ViewChild(matSort) sort: MatSort
это для меня должно быть то же самое
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort matSortActive="name" matSortDirection="asc">
На самом деле имя 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];
}
}
когда вы назначаете dataSource.sort = this.sort, вы должны оставить эту строку внутри ngAfterViewInit, а не внутри ngOnInit.
У меня была такая же проблема. Как только я сделал выше, он начал работать нормально.
Для этого может быть несколько причин, убедитесь, что вы правильно следуете документации и импортируете все необходимые модули, но если ошибка все еще сохраняется, то причины могут быть следующими.
Убедитесь, что вы добавили директиву matSort в таблицу и директиву mat-sort-header в ячейку.
Если ваша таблица находится в *ngIf, сортировка и нумерация страниц также не будут работать.
<table mat-table [dataSource]="dataSource" matSort (matSortChange)="announceSortChange($event)"> <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription="Sort by No">
Все вышеперечисленное мне не помогло. Я получаю такие даты, как «20.01.2020 13:20:30.400». Вот решение, которое мне помогло:
Добавленnew Date(item[property])
создание:
this.yourDataSource.sortingDataAccessor = (item: any, property) => {
if (property === 'date') {
return new Date(item[property]);
}
return item[property];
};
Меня устраивает!
Эта проблема в основном возникает при инициализации до. В демонстрации, найденной здесь, инициализируется статически, поэтому никаких проблем не возникает. Однако, когда вам нужно получать данные асинхронно, вам нужно дождаться ответа от вызова API, чтобы достичь и назначить
dataSource
прежде чем инициировать
sort
переменная экземпляра.