Sortowanie kolumn tabeli w Angular
Próbuję umożliwić sortowanie moich kolumn tabeli. Znalazłem ten tutorial tutaj:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s
Korzystając z tych informacji, otrzymałem następujące informacje:
Rura, która obsługuje sortowanie
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;
}
}
Oto komponent, który pomaga mi zbudować stół. Tutaj definiuję zmienną sortcolumn.
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();
}
}
Oto mój plik szablonu. Jak widać, przymocowałem sortrurkę do pętli, wypluwając rzędy stołu. Warto zauważyć, że sposób renderowania tabeli różni się od wideo. Na przykład jego dane są przechowywane w tablicy, ale moje są przechowywane w Firebase. Renderuje swoją tabelę dynamicznie, ale moja jest przypisana do określonej liczby kolumn. Zakoduję też na stałe nagłówki, ale on użył nazw zmiennych ze swojej tablicy do wygenerowania nagłówków tabeli. Nie jestem pewien, czy te różnice mogą przeszkadzać w działaniu.
<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>
Otrzymałem następujący błąd poniżej, ale udało mi się go naprawić, wstrzykując następujący kod do mojego pliku potoku: list = !!list ? list : [];
Teraz nie ma błędów, ale sortowanie nie działa zgodnie z oczekiwaniami. Po kliknięciu nagłówka tabeli nic się nie dzieje. Jak mogę to naprawić?
Odpowiedzi
zapomnij o fajce. sortowanie potokiem jest złą praktyką, prowadzi do błędnego kodu lub złej wydajności.
Zamiast tego użyj obserwabli.
najpierw zmień przyciski nagłówka szablonu, aby wywołać funkcję, a także upewnij się, że podajesz rzeczywiste nazwy właściwości, według których chcesz sortować, a nie zawartość nagłówka:
<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>
następnie wyciągnij funkcję sortowania i zaimportuj do komponentu:
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;
}
następnie napraw komponent:
// 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();
}
}
na koniec napraw ngFor:
<tr *ngFor="let s of stocks$ | async">
w ten sposób nie polegasz na magii ani wykrywaniu zmian. uruchamiasz swoje sortowanie, kiedy musi być uruchamiane przez obserwowalne
Myślę, że twoje wartości nie są przekazywane do rury:
Możesz spróbować:
<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
Wywołanie Asyn przed przypisaniem this.stocks$tabeli wartości załaduje wywołanie potoku
constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
this.stocks$ = this.updatesService.getStocks();
}
Szablon
<tbody *ngIf="stocks$"> <tr *ngFor="let s of stocks$ | sort : sortedColumn">
....
</tr>
</tbody>