Вопрос или проблема
Я работаю над проектом на Angular, где использую mat-table от Angular Material с включенной сортировкой. Функциональность сортировки работает, но стрелки в заголовках столбцов отображаются некорректно, когда я меняю сортировку с помощью кода TypeScript.
HTML:
<div class="cpage">
<div class="left-margin"></div>
<div class="left">
<form-card header="Левый" [enableBottomPadding]="true">
<button mat-raised-button (click)="actionB1()">B1</button>
</form-card>
</div>
<div class="gap-between"></div>
<div class="right">
<form-card header="ТаблицаДанных" [enableBottomPadding]="true">
<div style="width: 100%; max-height:90vh; overflow-x:auto">
<mat-table [dataSource]="dataSource" matSort #sort1="matSort">
<ng-container matColumnDef="position">
<mat-header-cell mat-sort-header *matHeaderCellDef> №</mat-header-cell>
<mat-cell *matCellDef="let element"> {{ element.position }}</mat-cell>
</ng-container>
<ng-container matColumnDef="name">
<mat-header-cell mat-sort-header *matHeaderCellDef> Имя</mat-header-cell>
<mat-cell *matCellDef="let element"> {{ element.name }}</mat-cell>
</ng-container>
<ng-container matColumnDef="weight">
<mat-header-cell mat-sort-header *matHeaderCellDef> Вес</mat-header-cell>
<mat-cell *matCellDef="let element"> {{ element.weight }}</mat-cell>
</ng-container>
<!-- дополнительные колонки веса -->
<ng-container matColumnDef="weight1">
<mat-header-cell *matHeaderCellDef> Вес</mat-header-cell>
<mat-cell *matCellDef="let element"> {{ element.weight }}</mat-cell>
</ng-container>
<ng-container matColumnDef="symbol">
<mat-header-cell *matHeaderCellDef> Символ</mat-header-cell>
<mat-cell *matCellDef="let element"> {{ element.symbol }}</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</div>
</form-card>
</div>
<div class="right-margin"></div>
</div>
TS:
import { AfterViewInit, ChangeDetectorRef, Component, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from "@angular/material/table";
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}
const ELEMENT_DATA: PeriodicElement[] = [
{ position: 1, name: 'Водород', weight: 1.0079, symbol: 'H' },
{ position: 2, name: 'Гелий', weight: 4.0026, symbol: 'He' },
{ position: 3, name: 'Литий', weight: 6.941, symbol: 'Li' },
{ position: 4, name: 'Бериллий', weight: 9.0122, symbol: 'Be' },
{ position: 5, name: 'Бор', weight: 10.811, symbol: 'B' },
{ position: 6, name: 'Углерод', weight: 12.0107, symbol: 'C' },
{ position: 7, name: 'Азот', weight: 14.0067, symbol: 'N' },
{ position: 8, name: 'Кислород', weight: 15.9994, symbol: 'O' },
{ position: 9, name: 'Фтор', weight: 18.9984, symbol: 'F' },
{ position: 10, name: 'Неон', weight: 20.1797, symbol: 'Ne' },
];
@Component({
selector: 'app-debug-table',
templateUrl: './debug-table.component.html',
styleUrls: ['./debug-table.component.scss']
})
export class DebugTableComponent implements AfterViewInit {
displayedColumns: string[] = ['position', 'name', 'weight', 'weight1', 'symbol'];
dataSource = new MatTableDataSource(ELEMENT_DATA);
@ViewChild('sort1', { static: false }) sort1: MatSort;
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.dataSource.sort = this.sort1;
}
actionB1() {
if (this.sort1.active == 'name') {
this.sort1.active="weight";
} else {
this.sort1.active="name";
}
this.dataSource.sort = this.sort1;
this.cdr.detectChanges();
}
}
Проблема: Сортировка работает, но стрелки сортировки рядом с заголовками столбцов обновляются некорректно. Нажатие кнопки “B1” меняет сортировку между “name” и “weight”, но стрелки на заголовках mat-sort-header, похоже, не следуют этой смене.
Что я попробовал:
Проверил, что модуль сортировки Angular Material импортирован.
Убедился, что matSort и mat-sort-header правильно добавлены к mat-table и заголовкам ячеек.
Вызвал detectChanges после обновления сортировки, чтобы обновить представление.
Кто-нибудь сталкивался с этой проблемой раньше? Есть идеи, как сделать так, чтобы стрелки отображались правильно?
Angular: 16
Angular Material: 16
Вы можете использовать метод sort
из dataSource.sort
, который принимает интерфейс ввода MatSortable
.
export declare interface MatSortable {
/** Идентификатор сортируемого столбца. */
id: string;
/** Начальное направление сортировки. */
start: SortDirection;
/** Отключить очистку состояния сортировки. */
disableClear: boolean;
}
export declare type SortDirection = 'asc' | 'desc' | '';
Тогда мы можем использовать метод таким образом.
actionB1() {
let active = null;
if (this.sort1.active == 'name') {
active="weight";
} else {
active="name";
}
this.dataSource.sort!.sort({
id: active,
start: 'asc',
disableClear: true,
});
// this.cdr.detectChanges();
}
Полный код:
HTML:
<button mat-raised-button (click)="actionB1()">B1</button>
<table
mat-table
matSort
[dataSource]="dataSource"
class="mat-elevation-z8"
(matSortChange)="sortData($event)"
>
<!--- Обратите внимание, что эти столбцы могут быть определены в любом порядке.
Фактически отображаемые столбцы задаются как свойство в определении строки" -->
<!-- Столбец Позиция -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef mat-sort-header="position">№</th>
<td mat-cell *matCellDef="let element">{{element.position}}</td>
</ng-container>
<!-- Столбец Имя -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name">Имя</th>
<td mat-cell *matCellDef="let element">{{element.name}}</td>
</ng-container>
<!-- Столбец Вес -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef mat-sort-header="weight">Вес</th>
<td mat-cell *matCellDef="let element">{{element.weight}}</td>
</ng-container>
<!-- Столбец Символ -->
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef mat-sort-header="symbol">Символ</th>
<td mat-cell *matCellDef="let element">{{element.symbol}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
TS:
import { Component, ViewChild, ChangeDetectorRef, inject } from '@angular/core';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
import { MatSortModule, Sort } from '@angular/material/sort';
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}
const ELEMENT_DATA: PeriodicElement[] = [
{ position: 1, name: 'Водород', weight: 1.0079, symbol: 'H' },
{ position: 2, name: 'Гелий', weight: 4.0026, symbol: 'He' },
{ position: 3, name: 'Литий', weight: 6.941, symbol: 'Li' },
{ position: 4, name: 'Бериллий', weight: 9.0122, symbol: 'Be' },
{ position: 5, name: 'Бор', weight: 10.811, symbol: 'B' },
{ position: 6, name: 'Углерод', weight: 12.0107, symbol: 'C' },
{ position: 7, name: 'Азот', weight: 14.0067, symbol: 'N' },
{ position: 8, name: 'Кислород', weight: 15.9994, symbol: 'O' },
{ position: 9, name: 'Фтор', weight: 18.9984, symbol: 'F' },
{ position: 10, name: 'Неон', weight: 20.1797, symbol: 'Ne' },
];
function compare(a: number | string, b: number | string, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
/**
* @title Базовое использование `<table mat-table>`
*/
@Component({
selector: 'table-basic-example',
styleUrl: 'table-basic-example.css',
templateUrl: 'table-basic-example.html',
standalone: true,
imports: [MatTableModule, MatSortModule],
})
export class TableBasicExample {
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource = new MatTableDataSource(ELEMENT_DATA);
cdr = inject(ChangeDetectorRef);
@ViewChild(MatSort, { static: false }) sort1: MatSort;
ngAfterViewInit() {
this.dataSource.sort = this.sort1;
}
actionB1() {
let active = null;
if (this.sort1.active == 'name') {
active="weight";
} else {
active="name";
}
this.dataSource.sort!.sort({
id: active,
start: 'asc',
disableClear: true,
});
// this.cdr.detectChanges();
}
sortData(sort: Sort) {
const data = this.dataSource.data.slice();
if (!sort.active || sort.direction === '') {
this.dataSource.data = data;
return;
}
this.dataSource.data = data.sort((a, b) => {
const isAsc = sort.direction === 'asc';
switch (sort.active) {
case 'name':
return compare(a.name, b.name, isAsc);
case 'weight':
return compare(a.weight, b.weight, isAsc);
case 'symbol':
return compare(a.symbol, b.symbol, isAsc);
default:
return 0;
}
});
}
}
Демо Stackblitz
Ответ или решение
Проблема: неправильное отображение стрелок сортировки в Angular Material Table
В процессе работы над проектом на Angular с использованием mat-table
из Angular Material, возникает ситуация, когда стрелки сортировки в заголовках столбцов отображаются некорректно. В вашем случае функциональность сортировки работает, однако при переключении сортировки с помощью кнопки "B1", стрелки не обновляются как ожидалось. Давайте подробно рассмотрим, как это исправить.
1. Обновление кода
HTML Разметка
Ваш оригинальный код HTML выглядит достаточно правильно, с корректным использованием mat-sort-header
. Но давайте убедимся, что мы правильно определили заголовки с сортировкой:
<mat-header-cell mat-sort-header="name" *matHeaderCellDef> Name </mat-header-cell>
<mat-header-cell mat-sort-header="weight" *matHeaderCellDef> Weight </mat-header-cell>
Вы сделали это верно, но возможно нужно проверить, что все заголовки столбцов, которые должны сортироваться, правильно включают атрибут mat-sort-header
.
Проблема в TypeScript коде
В вашем методе actionB1
, в случае переключения сортировки, вы используете код:
this.sort1.active = "weight"; // или "name"
this.dataSource.sort = this.sort1;
this.cdr.detectChanges(); // Необходимо ли?
Ваш код присваивает новое значение active
свойству sort1
, но при этом в методе actionB1
не была правильно обновлена сортировка самой таблицы. Рекомендуется использовать метод sort
объекта dataSource.sort
, который принимает объект типа MatSortable
.
2. Исправленный вариант кода
Обновим метод actionB1
следующим образом:
actionB1() {
let active = this.sort1.active === 'name' ? 'weight' : 'name';
this.dataSource.sort.sort({
id: active,
start: 'asc', // предполагаем, что сортировка всегда будет восходящей
disableClear: false, // можно оставить false, если хотите, чтобы сортировка не сбрасывалась
});
}
3. Проверка импорта модулей
Убедитесь, что в вашем модуле импортированы все необходимые модули Angular Material. Проверьте, что в вашем основном модуле (например, AppModule
) правильно импортирован MatTableModule
и MatSortModule
.
import { MatTableModule } from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
@NgModule({
imports: [
MatTableModule,
MatSortModule,
// другие нужные модули
],
declarations: [
// ваши компоненты
],
})
export class AppModule { }
4. Окончательные замечания
Подводя итог, убедитесь, что вы используете правильные методы для обновления сортировки в таблице. Исправленный код должен правильно обрабатывать отображение стрелок. Также полезно проверить в браузере инструменты разработчика на наличие ошибок или предупреждений, которые могут указать на проблемы с рендерингом.
Обратите внимание, что использование detectChanges()
может быть необходимо в некоторых случаях, но в данном контексте его использование может быть лишним, так как Angular должен самостоятельно отслеживать изменения в компоненте.
Если после внесения этих изменений проблема останется, стоит дополнительно проверить стили и убедиться, что конфликтов со сторонними библиотеками или CSS не возникает.