Ordenar columnas de la tabla en Angular
Estoy tratando de ordenar las columnas de mi tabla. Encontré este tutorial aquí:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s
Usando esa información, terminé con lo siguiente:
Una tubería que maneja la clasificación.
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;
}
}
Aquí está el componente que me ayuda a construir mi tabla. Aquí defino 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();
}
}
Aquí está mi archivo de plantilla. Como puede ver, he conectado mi sorttubería a mi bucle, escupiendo las filas de la mesa. Vale la pena señalar que la forma en que renderizo la tabla difiere del video. Por ejemplo, sus datos se almacenan en una matriz, pero los míos se almacenan en Firebase. Está renderizando su tabla dinámicamente, pero la mía está fijada a un cierto número de columnas. También estoy codificando los encabezados, pero usó los nombres de las variables de su matriz para generar los encabezados de la tabla. No estoy seguro de si estas diferencias podrían estar impidiendo que las cosas funcionen.
<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>
Recibí el siguiente error a continuación, pero pude solucionarlo inyectando el siguiente código en mi archivo de tubería: list = !!list ? list : [];
Ahora no hay errores, pero la clasificación no funciona como se esperaba. Cuando hago clic en el encabezado de la tabla, no pasa nada. ¿Cómo puedo arreglar esto?
Respuestas
olvídate de la pipa. ordenar a través de tubería es una mala práctica, conduce a un código defectuoso o un mal rendimiento.
En su lugar, use observables.
Primero cambie los botones de encabezado de su plantilla para llamar a una función, y también asegúrese de que está ingresando los nombres de propiedad reales por los que desea ordenar, en lugar del contenido del encabezado:
<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>
luego, extraiga su función de clasificación e importe a su componente:
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;
}
luego arregle su componente:
// 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();
}
}
finalmente, arregla tu ngFor:
<tr *ngFor="let s of stocks$ | async">
de esta manera, no dependerá de la magia ni de la detección de cambios. está activando su tipo cuando necesita activarse a través de observables
Creo que sus valores no se transmiten a la tubería:
Puedes intentar:
<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
La llamada asíncrona aquí antes de asignar la this.stocks$tabla de valores cargará la tubería se llamará
constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
this.stocks$ = this.updatesService.getStocks();
}
Modelo
<tbody *ngIf="stocks$"> <tr *ngFor="let s of stocks$ | sort : sortedColumn">
....
</tr>
</tbody>