Tri des colonnes de table dans Angular
J'essaye de rendre mes colonnes de table triables. J'ai trouvé ce tutoriel ici:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s
En utilisant ces informations, je me suis retrouvé avec ce qui suit:
Un tuyau qui gère le tri
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;
}
}
Voici le composant qui m'aide à construire ma table. Ici, je définis la variable 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();
}
}
Voici mon fichier modèle. Comme vous pouvez le voir, j'ai attaché mon sorttuyau à ma boucle, crachant les lignes du tableau. Il convient de noter que la façon dont je rend le tableau diffère de la vidéo. Par exemple, ses données sont stockées dans un tableau, mais les miennes sont stockées sur Firebase. Il rend sa table dynamiquement, mais la mienne est fixée à un certain nombre de colonnes. Je codifie également en dur les en-têtes, mais il a utilisé les noms de variables de son tableau pour générer les en-têtes de table. Je ne sais pas si ces différences pourraient empêcher les choses de fonctionner.
<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>
J'obtenais l'erreur suivante ci-dessous, mais j'ai pu la corriger en injectant le code suivant dans mon fichier pipe: list = !!list ? list : [];
Il n'y a maintenant aucune erreur, mais le tri ne fonctionne pas comme prévu. Lorsque je clique sur l'en-tête du tableau, rien ne se passe. Comment puis-je réparer cela?
Réponses
oubliez la pipe. le tri via un tube est une mauvaise pratique, conduit à un code bogué ou à de mauvaises performances.
Utilisez plutôt des observables.
Commencez par modifier les boutons d'en-tête de votre modèle pour appeler une fonction, et assurez-vous également que vous fournissez les noms de propriété réels que vous souhaitez trier, plutôt que le contenu de l'en-tête:
<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>
puis, sortez votre fonction de tri et importez dans votre composant:
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;
}
puis réparez votre composant:
// 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();
}
}
enfin, corrigez votre ngFor:
<tr *ngFor="let s of stocks$ | async">
de cette façon, vous ne comptez pas sur la magie ou la détection de changement. vous déclenchez votre tri quand il doit se déclencher via des observables
Je pense que vos valeurs ne sont pas transmises dans le tube:
Peux-tu essayer:
<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
L'appel Asyn ici avant d'affecter la this.stocks$table de valeurs chargera le tube sera appelé
constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
this.stocks$ = this.updatesService.getStocks();
}
Modèle
<tbody *ngIf="stocks$"> <tr *ngFor="let s of stocks$ | sort : sortedColumn">
....
</tr>
</tbody>