How to find the count of items in an ngFor after t

2019-01-26 03:27发布

I have an ngFor creating rows in a table that is both filtered and paged.

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

There is another element on the page that displays the number of records displayed. These elements are initially bound to the data.Results' length.

How do I get the length of the data that is displayed after the filter pipe has been applied so that I can display it correctly. None of the provided local variables in ngFor seem to account for this.

7条回答
我只想做你的唯一
2楼-- · 2019-01-26 03:57

One way is to use template variables with @ViewChildren()

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

ngAfterViewInit() {
  console.log(this.createdItems.toArray().length);
}
查看更多
倾城 Initia
3楼-- · 2019-01-26 03:58

So I found a workaround for this.

I created a pipe which takes an object reference and updates a property with the count currently passing through the pipe.

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

Then in your view link up a pagination component like so.

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

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

Once that count property is extracted from the pipe either to the current component or a child component you can do anything with it.

查看更多
forever°为你锁心
4楼-- · 2019-01-26 04:03

I found the simplest solution to be the following:

  1. In your component: add a field that will hold the current count
  filterMetadata = { count: 0 };
  1. In your template: add the filterMetadata as a parameter to the filter pipe
  <tr *ngFor="#d of data.results | filter:filterText:filterMetadata | pagination:resultsPerPage:currentPage">
  1. interpolate filterMetadata.count into the element that displays the number of records displayed.
  <span> {{filterMetadata.count}} records displayed</span>
  1. In the filter pipe, update the filterMetadata.count field when done with filtering
  transform(items, ..., filterMetadata) {
    // filtering
    let filteredItems = filter(items);

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

This solution still uses pure pipes, so there are no performance degradations. If you have multiple pipes that do filtering, the filterMetadata should be added as a parameter to all of them because angular stops calling the pipe sequence as soon as the a pipe returns an empty array, so you can't just add it to the first or last filtering pipe.

查看更多
叛逆
5楼-- · 2019-01-26 04:09

That is not exactly the purpose of the original question, but I was also looking for a way to display the count of items once that all pipes have been applied. By combining the index and last values provided by ngFor, I found this other solution :

<div *ngFor="#item of (items | filter); #i = index; #last = last">
...
  <div id="counter_id" *ngIf="last">{{index + 1}} results</div>
</div>
查看更多
Juvenile、少年°
6楼-- · 2019-01-26 04:11

You can get the count of the items by transforming the array within a pipe.

The idea is that the pipe would transform the array into another array where each element has an item property, and a parent property representing the filtered (or original) array:

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

Here is how it would be used:

 <tr *ngFor="#d of data.results | 
        filter:filterText |
        pagination:resultsPerPage:currentPage | 
        withParent">
        Count:  {{d.parent.length }}
        Item:  {{ d.item.name}}
 </tr>
查看更多
虎瘦雄心在
7楼-- · 2019-01-26 04:20

I came across the same problem, although @bixelbits 's answer was approved, but I didn't find it ideal, specially for large data.

Instead of returning the original array in each element, I believe it's better just avoid Pipes for this problem, at least with the current Angular 2 implementation (rc4).

A better solution would be using normal component's function to filter the data, something likes bellow:

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

Then, in the template:

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

Unless you really want to use Pipes, I would recommend you to avoid them in situations like the above example.

查看更多
登录 后发表回答