web-dev-qa-db-ja.com

パイプが適用された後にngFor内のアイテムの数を見つける方法

フィルタリングとページングの両方が行われているテーブルに行を作成するngForがあります。

<tr *ngFor="#d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">

ページには、表示されているレコードの数を表示する別の要素があります。これらの要素は、最初はdata.Resultsの長さにバインドされます。

フィルターパイプが適用された後に表示されるデータの長さを取得して、正しく表示できるようにする方法を教えてください。 ngForで提供されるローカル変数はどれもこれを考慮していないようです。

21
ghawkes

パイプ内で配列を変換することにより、アイテムの数を取得できます。

アイデアは、パイプが配列を別の配列に変換し、各要素がアイテムプロパティと、フィルター処理された(または元の)配列を表す親プロパティを持つことです。

@Pipe({ name: 'withParent', pure: false })
export class WithParentPipe implements PipeTransform {
    transform(value: Array<any>, args: any[] = null): any {

        return value.map(t=> {
            return {
                item: t,
                parent: value
            }
        });
    }
} 

使用方法は次のとおりです。

 <tr *ngFor="#d of data.results | 
        filter:filterText |
        pagination:resultsPerPage:currentPage | 
        withParent">
        Count:  {{d.parent.length }}
        Item:  {{ d.item.name}}
 </tr>
7
pixelbits

1つの方法は、@ViewChildren()でテンプレート変数を使用することです

<tr #myVar *ngFor="let d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">
@ViewChildren('myVar') createdItems;

ngAfterViewInit() {
  console.log(this.createdItems.toArray().length);
}
14

これは元の質問の正確な目的ではありませんが、すべてのパイプが適用された後にアイテムの数を表示する方法も探していました。 ngForが提供するインデックスと最後の値を組み合わせることで、次の解決策が見つかりました。

<div *ngFor="#item of (items | filter); #i = index; #last = last">
...
  <div id="counter_id" *ngIf="last">{{index + 1}} results</div>
</div>
8
jean-baptiste

最も簡単な解決策は次のとおりです。

  1. コンポーネント内:現在のカウントを保持するフィールドを追加します
  filterMetadata = { count: 0 };
  1. テンプレートで:filterMetadataをパラメーターとしてフィルターパイプに追加します。
  <tr *ngFor="#d of data.results | filter:filterText:filterMetadata | pagination:resultsPerPage:currentPage">
  1. filterMetadata.countを、表示されるレコードの数を表示する要素に挿入します。
  <span> {{filterMetadata.count}} records displayed</span>
  1. フィルターパイプで、フィルター処理が完了したらfilterMetadata.countフィールドを更新します
  transform(items, ..., filterMetadata) {
    // filtering
    let filteredItems = filter(items);

    filterMetadata.count = filteredItems.length;
    return filteredItems;
  }

このソリューションはまだ純粋なパイプを使用しているため、パフォーマンスの低下はありません。フィルタリングを行う複数のパイプがある場合、angularはパイプが空の配列を返すとすぐにパイプシーケンスの呼び出しを停止するため、filterMetadataをすべてのパイプにパラメーターとして追加する必要があります。最初または最後のフィルタリングパイプに追加することはできません。

6
Soufiane Sakhi

@bixelbitsの答えは承認されましたが、同じ問題に遭遇しましたが、特に大規模なデータには理想的ではありませんでした。

各要素で元の配列を返す代わりに、少なくとも現在のAngular 2実装(rc4)で、この問題に対してPipesを避ける方が良いと思います。

より良い解決策は、通常のコンポーネントの機能を使用してデータをフィルタリングすることです。以下のようなものがあります。

// mycomponent.component.ts  
filter() {
  let arr = this.data.filter(
      // just an example
      item => item.toLowerCase().includes(
        // term is a local variable I get it's from <input> 
        this.term.toLowerCase()
      )
    );
  this.filteredLength = arr.length;
  return arr;
}

次に、テンプレートで:

<ul>
  <li *ngFor="let el of filter()"> 
    {{ el | json }}
  </li>
</ul>
// mycomponent.component.html
<p > There are {{ filteredLength }} element(s) in this page</p>

Pipesを本当に使用したくない場合を除き、上記の例のような状況ではそれらを避けることをお勧めします。

5
Ahmed T. Ali

だから私はこのための回避策を見つけました。

オブジェクト参照を取得し、現在パイプを通過しているカウントでプロパティを更新するパイプを作成しました。

@Pipe({
    name: 'count'
})

export class CountPipe implements PipeTransform {
    transform(value, args) {
        if (!args) {
            return value;
        }else if (typeof args === "object"){

            //Update specified object key with count being passed through
            args.object[args.key] = value.length;

            return value;

        }else{
            return value;
        }
    }
}

次に、ビュー内でページネーションコンポーネントをリンクします。

pagination-controls(#controls="", [items]="items.length", (onChange)="o") 

tbody
    tr(*ngFor=`let item of items
        | filter_pipe: { .... }
        | count: { object: controls , key: 'items' }
        | pagination_pipe: { ... } `)

そのcountプロパティがパイプから現在のコンポーネントまたは子コンポーネントのいずれかに抽出されると、それで何でもできます。

4
Victor96

私の場合、フィルタリングされた要素を実行して、分析を実行する必要がありました。

私の解決策は、単に関数を渡し、最後のパイプで配列を返すことです。また、特にこのためにパイプを作成することもできますが、フィルターの最後のパイプに追加しました:

HTML

<divclass="card" *ngFor="let job of jobs | employee:jobFilter.selectedEmployee | managerStatus:jobFilter.selectedStatus | dateOrder:jobFilter"> 

成分

this.jobFilter = {
  jobStatuses: {},  // status labels
  ordering: 'asc',
  selectedEmployee: {},
  selectedStatus: {}, // results status
  fn: this.parseFilteredArr
};

parseFilteredArr(arr) {
  console.log(arr);
}

パイプ

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'dateOrder'
})
export class DateOrderPipe implements PipeTransform {
 transform(value: any, args?: any): any {
   const arr = Array.isArray(value)
    ? value.reverse()
    : value;
  args.fn(arr);
  return arr;
}
}

あなたが見ることができるように、私はパイプ内の関数を呼び出しました

args.fn(arr);

コントローラーで処理できるようになりました。

0
dale