Сортировка столбцов таблицы в Angular
Я пытаюсь настроить сортировку столбцов таблицы. Я нашел этот учебник здесь:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s
Используя эту информацию, я получил следующее:
Труба, которая обрабатывает сортировку
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'sort',
pure: true
})
export class TableSortPipe implements PipeTransform {
transform(list: any[], column:string): any[] {
let sortedArray = list.sort((a,b)=>{
if(a[column] > b[column]){
return 1;
}
if(a[column] < b[column]){
return -1;
}
return 0;
})
return sortedArray;
}
}
Вот компонент, который помогает мне построить мою таблицу. Здесь я определяю переменную sortedColumn.
import { NavbarService } from './../navbar/navbar.service';
import { LiveUpdatesService } from './live-updates.service';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'app-live-updates',
templateUrl: './live-updates.component.html',
styleUrls: ['./sass/live-updates.component.scss']
})
export class LiveUpdatesComponent implements OnInit{
stocks$: Observable<any[]>; sortedColumn: string; constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) { this.stocks$ = this.updatesService.getStocks();
}
ngOnInit() {
this.nav.show();
}
}
Вот мой файл шаблона. Как видите, я прикрепил sortтрубку к петле, выплевывая строки таблицы. Стоит отметить, что способ рендеринга таблицы отличается от видео. Например, его данные хранятся в массиве, а мои - в Firebase. Он отображает свою таблицу динамически, но моя привязана к определенному количеству столбцов. Я также жестко кодирую заголовки, но он использовал имена переменных из своего массива для создания заголовков таблиц. Я не уверен, могут ли эти различия мешать работе.
<section class="score-cards">
<app-score-cards></app-score-cards>
</section>
<section class="live-updates-wrapper">
<div class="table-wrapper">
<table class="stock-updates">
<thead>
<tr>
<th class="ticker-fixed">Ticker</th>
<th><a (click)="sortedColumn = $any($event.target).textContent">Ask Price</a></th>
<th><a (click)="sortedColumn = $any($event.target).textContent">Tax Value</a></th>
<th><a (click)="sortedColumn = $any($event.target).textContent">Est. Value</a></th>
<th><a (click)="sortedColumn = $any($event.target).textContent">Location</a></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let s of stocks$ | async | sort : sortedColumn">
<td class="ticker-fixed">
<a target="_blank" href="https://robinhood.com/stocks/{{ s.TICKER }}">{{ s.TICKER }}</a>
<span class="sp500">{{ s.sp500_flag }}S&P</span>
</td>
<td>{{ s.CLOSE }}</td>
<td>{{ s.tax_diff }}</td>
<td>{{ s.MarketCap }}</td>
<td>{{ s.Sector }}</td>
</tr>
</tbody>
</table>
</div>
</section>
Я получал следующую ошибку ниже, но смог исправить ее, вставив следующий код в свой файл трубы: list = !!list ? list : [];
Теперь ошибок нет, но сортировка не работает должным образом. Когда я нажимаю на заголовок таблицы, ничего не происходит. Как я могу это исправить?
Ответы
забудь трубку. сортировка по конвейеру - плохая практика, приводит либо к ошибкам в коде, либо к плохой производительности.
Вместо этого используйте наблюдаемые.
сначала измените кнопки заголовка шаблона, чтобы вызвать функцию, а также убедитесь, что вы вводите фактические имена свойств, по которым хотите сортировать, а не содержимое заголовка:
<th><a (click)="sortOn('CLOSE')">Ask Price</a></th>
<th><a (click)="sortOn('tax_diff')">Tax Value</a></th>
<th><a (click)="sortOn('MarketCap')">Est. Value</a></th>
<th><a (click)="sortOn('Sector')">Location</a></th>
затем вытащите функцию сортировки и импортируйте в свой компонент:
export function sortByColumn(list: any[] | undefined, column:string, direction = 'desc'): any[] {
let sortedArray = (list || []).sort((a,b)=>{
if(a[column] > b[column]){
return (direction === 'desc') ? 1 : -1;
}
if(a[column] < b[column]){
return (direction === 'desc') ? -1 : 1;
}
return 0;
})
return sortedArray;
}
затем исправьте свой компонент:
// rx imports
import { combineLatest, BehaviorSubject } from 'rxjs';
import { map, scan } from 'rxjs/operators';
...
export class LiveUpdatesComponent implements OnInit{
stocks$: Observable<any[]>; // make this a behavior subject instead sortedColumn$ = new BehaviorSubject<string>('');
// the scan operator will let you keep track of the sort direction
sortDirection$ = this.sortedColumn$.pipe(
scan<string, {col: string, dir: string}>((sort, val) => {
return sort.col === val
? { col: val, dir: sort.dir === 'desc' ? 'asc' : 'desc' }
: { col: val, dir: 'desc' }
}, {dir: 'desc', col: ''})
)
constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
// combine observables, use map operator to sort
this.stocks$ = combineLatest(this.updatesService.getStocks(), this.sortDirection$).pipe(
map(([list, sort]) => !sort.col ? list : sortByColumn(list, sort.col, sort.dir))
);
}
// add this function to trigger subject
sortOn(column: string) {
this.sortedColumn$.next(column);
}
ngOnInit() {
this.nav.show();
}
}
наконец, исправьте ngFor:
<tr *ngFor="let s of stocks$ | async">
таким образом, вы не полагаетесь на магию или обнаружение изменений. вы запускаете свой вид, когда он должен запускаться через наблюдаемые
Я думаю, что ваши ценности не передаются в трубу:
Ты можешь попробовать:
<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
Асинхронный вызов здесь перед назначением this.stocks$таблицы значений загрузит канал, который будет вызван
constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
this.stocks$ = this.updatesService.getStocks();
}
Шаблон
<tbody *ngIf="stocks$"> <tr *ngFor="let s of stocks$ | sort : sortedColumn">
....
</tr>
</tbody>