Sortieren von Tabellenspalten in Angular

Aug 26 2020

Ich versuche, meine Tabellenspalten sortierbar zu machen. Ich habe dieses Tutorial hier gefunden:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s

Mit diesen Informationen kam ich zu folgendem Ergebnis:

Ein Rohr, das die Sortierung übernimmt

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

Hier ist die Komponente, mit der ich meine Tabelle erstellen kann. Hier definiere ich die sortedColumn-Variable.

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

Hier ist meine Vorlagendatei. Wie Sie sehen können, habe ich meine sortPipe an meiner Schleife befestigt und die Tabellenzeilen ausgespuckt. Es ist erwähnenswert, dass sich die Art und Weise, wie ich die Tabelle rendere, vom Video unterscheidet. Zum Beispiel werden seine Daten in einem Array gespeichert, meine jedoch in Firebase. Er rendert seine Tabelle dynamisch, aber meine ist auf eine bestimmte Anzahl von Spalten festgelegt. Ich codiere auch die Header fest, aber er hat die Variablennamen aus seinem Array verwendet, um die Tabellenheader zu generieren. Ich bin mir nicht sicher, ob diese Unterschiede verhindern könnten, dass etwas funktioniert.

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

Ich habe den folgenden Fehler erhalten, konnte ihn jedoch beheben, indem ich den folgenden Code in meine Pipe-Datei eingefügt habe: list = !!list ? list : [];

Jetzt gibt es keine Fehler, aber die Sortierung funktioniert nicht wie erwartet. Wenn ich auf die Tabellenüberschrift klicke, passiert nichts. Wie kann ich das beheben?

Antworten

2 bryan60 Aug 28 2020 at 19:17

Vergiss die Pfeife. Das Sortieren per Pipe ist eine schlechte Praxis und führt entweder zu fehlerhaftem Code oder zu schlechter Leistung.

Verwenden Sie stattdessen Observablen.

Ändern Sie zuerst die Schaltflächen Ihrer Vorlagenkopfzeilen, um eine Funktion aufzurufen, und stellen Sie außerdem sicher, dass Sie die tatsächlichen Eigenschaftsnamen eingeben, nach denen Sie sortieren möchten, und nicht den Inhalt der Kopfzeilen:

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

Ziehen Sie dann Ihre Sortierfunktion heraus und importieren Sie sie in Ihre Komponente:

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

Dann reparieren Sie Ihre Komponente:

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

Schließlich beheben Sie Ihre ngFor:

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

Auf diese Weise verlassen Sie sich nicht auf Magie oder Änderungserkennung. Sie lösen Ihre Sortierung aus, wenn sie über Observablen ausgelöst werden muss

2 Ondie Aug 26 2020 at 16:01

Ich denke, Ihre Werte werden nicht an die Pipe weitergegeben:

Kannst du es versuchen:

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

Asyn-Aufruf wird hier vor dem Zuweisen der this.stocks$Wertetabelle geladen. Die Pipe wird aufgerufen

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

Vorlage

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