Sortowanie kolumn tabeli w Angular

Aug 26 2020

Próbuję umożliwić sortowanie moich kolumn tabeli. Znalazłem ten tutorial tutaj:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s

Korzystając z tych informacji, otrzymałem następujące informacje:

Rura, która obsługuje sortowanie

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;
      }
    
    }

Oto komponent, który pomaga mi zbudować stół. Tutaj definiuję zmienną 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();
  }
}

Oto mój plik szablonu. Jak widać, przymocowałem sortrurkę do pętli, wypluwając rzędy stołu. Warto zauważyć, że sposób renderowania tabeli różni się od wideo. Na przykład jego dane są przechowywane w tablicy, ale moje są przechowywane w Firebase. Renderuje swoją tabelę dynamicznie, ale moja jest przypisana do określonej liczby kolumn. Zakoduję też na stałe nagłówki, ale on użył nazw zmiennych ze swojej tablicy do wygenerowania nagłówków tabeli. Nie jestem pewien, czy te różnice mogą przeszkadzać w działaniu.

<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>

Otrzymałem następujący błąd poniżej, ale udało mi się go naprawić, wstrzykując następujący kod do mojego pliku potoku: list = !!list ? list : [];

Teraz nie ma błędów, ale sortowanie nie działa zgodnie z oczekiwaniami. Po kliknięciu nagłówka tabeli nic się nie dzieje. Jak mogę to naprawić?

Odpowiedzi

2 bryan60 Aug 28 2020 at 19:17

zapomnij o fajce. sortowanie potokiem jest złą praktyką, prowadzi do błędnego kodu lub złej wydajności.

Zamiast tego użyj obserwabli.

najpierw zmień przyciski nagłówka szablonu, aby wywołać funkcję, a także upewnij się, że podajesz rzeczywiste nazwy właściwości, według których chcesz sortować, a nie zawartość nagłówka:

<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>

następnie wyciągnij funkcję sortowania i zaimportuj do komponentu:

  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;
  }

następnie napraw komponent:

// 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();
  }
}

na koniec napraw ngFor:

<tr *ngFor="let s of stocks$ | async">

w ten sposób nie polegasz na magii ani wykrywaniu zmian. uruchamiasz swoje sortowanie, kiedy musi być uruchamiane przez obserwowalne

2 Ondie Aug 26 2020 at 16:01

Myślę, że twoje wartości nie są przekazywane do rury:

Możesz spróbować:

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

Wywołanie Asyn przed przypisaniem this.stocks$tabeli wartości załaduje wywołanie potoku

 constructor(private updatesService: LiveUpdatesService, public nav: NavbarService) {
    this.stocks$ = this.updatesService.getStocks();
  }

Szablon

 <tbody *ngIf="stocks$"> <tr *ngFor="let s of stocks$ | sort : sortedColumn">
          ....
        </tr>
  </tbody>