Ordenar columnas de la tabla en Angular

Aug 26 2020

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

2 bryan60 Aug 28 2020 at 19:17

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

2 Ondie Aug 26 2020 at 16:01

Creo que sus valores no se transmiten a la tubería:

Puedes intentar:

<tr *ngFor="let s of ((stocks$ | async) | sort : sortedColumn)">
1 karthicvel Sep 02 2020 at 12:02

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>