Angularでのテーブル列の並べ替え

Aug 26 2020

テーブルの列を並べ替え可能にしようとしています。私はここでこのチュートリアルを見つけました:https://www.youtube.com/watch?v=UzRuerCoZ1E&t=715s

その情報を使用して、私は次のことになりました。

仕分けを処理するパイプ

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

これが、テーブルの作成に役立つコンポーネントです。ここでは、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();
  }
}

これが私のテンプレートファイルです。ご覧のとおり、sortパイプをループに接続して、テーブルの行を吐き出しました。テーブルのレンダリング方法がビデオとは異なることに注意してください。たとえば、彼のデータは配列に保存されていますが、私のデータはFirebaseに保存されています。彼はテーブルを動的にレンダリングしていますが、私のものは特定の数の列に固定されています。私もヘッダーをハードコーディングしていますが、彼は配列の変数名を使用してテーブルヘッダーを生成しました。これらの違いが物事の機能を妨げているのかどうかはわかりません。

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

以下のエラーが発生していましたが、パイプファイルに次のコードを挿入して修正することができました。 list = !!list ? list : [];

これでエラーは発生しませんが、ソートが期待どおりに機能していません。テーブルヘッダーをクリックしても、何も起こりません。どうすればこれを修正できますか?

回答

2 bryan60 Aug 28 2020 at 19:17

パイプを忘れてください。パイプを介した並べ替えは悪い習慣であり、バグのあるコードまたはパフォーマンスの低下につながります。

代わりにオブザーバブルを使用してください。

まず、テンプレートヘッダーボタンを変更して関数を呼び出します。また、ヘッダーコンテンツではなく、並べ替える実際のプロパティ名を入力していることを確認します。

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

次に、並べ替え関数を引き出して、コンポーネントにインポートします。

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

次に、コンポーネントを修正します。

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

最後に、あなたのngFor:を修正します

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

このように、あなたは魔法や変化の検出に依存していません。オブザーバブルを介してトリガーする必要があるときに、ソートをトリガーします

2 Ondie Aug 26 2020 at 16:01

私はあなたの価値観がパイプに渡されていないと思います:

あなたは試すことができます:

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

this.stocks$テーブルを割り当てる前にここでAsynを呼び出すと、ロードパイプが呼び出されます

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

テンプレート

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