Classificando as colunas da tabela em Angular
Estou tentando tornar as colunas da minha tabela classificáveis. Encontrei este tutorial aqui:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s
Usando essas informações, acabei com o seguinte:
Um tubo que lida com a classificação
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;
}
}
Este é o componente que me ajuda a construir minha mesa. Aqui eu defino a variável 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();
}
}
Aqui está meu arquivo de modelo. Como você pode ver, prendi meu sortcachimbo ao meu loop, cuspindo as linhas da mesa. É importante notar que a maneira como estou renderizando a tabela é diferente do vídeo. Por exemplo, seus dados são armazenados em uma matriz, mas os meus são armazenados no Firebase. Ele está renderizando sua tabela dinamicamente, mas a minha está fixada em um certo número de colunas. Também estou codificando os cabeçalhos, mas ele usou os nomes das variáveis de sua matriz para gerar os cabeçalhos da tabela. Não tenho certeza se essas diferenças podem estar impedindo as coisas de funcionar.
<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>
Eu estava recebendo o seguinte erro abaixo, mas consegui corrigi-lo injetando o seguinte código no meu arquivo pipe: list = !!list ? list : [];
Agora não há erros, mas a classificação não está funcionando conforme o esperado. Quando clico no cabeçalho da mesa, nada acontece. Como posso consertar isso?
Respostas
esqueça o cachimbo. classificar por pipe é uma prática ruim, pois leva a um código com erros ou a um desempenho ruim.
Em vez disso, use observáveis.
primeiro altere os botões do cabeçalho do modelo para chamar uma função e também certifique-se de alimentar os nomes das propriedades reais pelos quais deseja classificar, em vez do conteúdo do cabeçalho:
<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>
em seguida, retire sua função de classificação e importe para seu 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;
}
em seguida, conserte seu 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, conserte o seu ngFor:
<tr *ngFor="let s of stocks$ | async">
dessa forma, você não está contando com magia ou detecção de alterações. você está acionando sua classificação quando precisa ser acionada por meio de observáveis
Acho que seus valores não são transmitidos para o tubo:
Você pode tentar:
<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
A chamada Asyn aqui antes de atribuir a this.stocks$tabela de valor carregará o tubo será chamado
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>