Sortieren von Tabellenspalten in Angular
Ich versuche, meine Tabellenspalten sortierbar zu machen. Ich habe dieses Tutorial hier gefunden:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s
Mit diesen Informationen kam ich zu folgendem Ergebnis:
Ein Rohr, das die Sortierung übernimmt
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;
}
}
Hier ist die Komponente, mit der ich meine Tabelle erstellen kann. Hier definiere ich die sortedColumn-Variable.
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();
}
}
Hier ist meine Vorlagendatei. Wie Sie sehen können, habe ich meine sortPipe an meiner Schleife befestigt und die Tabellenzeilen ausgespuckt. Es ist erwähnenswert, dass sich die Art und Weise, wie ich die Tabelle rendere, vom Video unterscheidet. Zum Beispiel werden seine Daten in einem Array gespeichert, meine jedoch in Firebase. Er rendert seine Tabelle dynamisch, aber meine ist auf eine bestimmte Anzahl von Spalten festgelegt. Ich codiere auch die Header fest, aber er hat die Variablennamen aus seinem Array verwendet, um die Tabellenheader zu generieren. Ich bin mir nicht sicher, ob diese Unterschiede verhindern könnten, dass etwas funktioniert.
<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>
Ich habe den folgenden Fehler erhalten, konnte ihn jedoch beheben, indem ich den folgenden Code in meine Pipe-Datei eingefügt habe: list = !!list ? list : [];
Jetzt gibt es keine Fehler, aber die Sortierung funktioniert nicht wie erwartet. Wenn ich auf die Tabellenüberschrift klicke, passiert nichts. Wie kann ich das beheben?
Antworten
Vergiss die Pfeife. Das Sortieren per Pipe ist eine schlechte Praxis und führt entweder zu fehlerhaftem Code oder zu schlechter Leistung.
Verwenden Sie stattdessen Observablen.
Ändern Sie zuerst die Schaltflächen Ihrer Vorlagenkopfzeilen, um eine Funktion aufzurufen, und stellen Sie außerdem sicher, dass Sie die tatsächlichen Eigenschaftsnamen eingeben, nach denen Sie sortieren möchten, und nicht den Inhalt der Kopfzeilen:
<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>
Ziehen Sie dann Ihre Sortierfunktion heraus und importieren Sie sie in Ihre Komponente:
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;
}
Dann reparieren Sie Ihre Komponente:
// 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();
}
}
Schließlich beheben Sie Ihre ngFor:
<tr *ngFor="let s of stocks$ | async">
Auf diese Weise verlassen Sie sich nicht auf Magie oder Änderungserkennung. Sie lösen Ihre Sortierung aus, wenn sie über Observablen ausgelöst werden muss
Ich denke, Ihre Werte werden nicht an die Pipe weitergegeben:
Kannst du es versuchen:
<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
Asyn-Aufruf wird hier vor dem Zuweisen der this.stocks$Wertetabelle geladen. Die Pipe wird aufgerufen
constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
this.stocks$ = this.updatesService.getStocks();
}
Vorlage
<tbody *ngIf="stocks$"> <tr *ngFor="let s of stocks$ | sort : sortedColumn">
....
</tr>
</tbody>