How to find the count of items in an ngFor after the pipes have been applied

前端 未结 9 1639
面向向阳花
面向向阳花 2020-12-06 04:18

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



        
相关标签:
9条回答
  • 2020-12-06 04:55

    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>
    
    0 讨论(0)
  • 2020-12-06 05:01

    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>
    
    0 讨论(0)
  • 2020-12-06 05:02

    In my case i needed to run through the filtered elements and run some analysis.

    My Solutions is to simply pass in a function and return the array on the last pipe. You could also just create a pipe especially for this but i have added it to my last pipe in the filters:

    HTML

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

    Component

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

    Pipe

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

    As you can see i have called the function in the pipe

    args.fn(arr);

    and now can process it in the controller.

    0 讨论(0)
  • 2020-12-06 05:14

    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.

    0 讨论(0)
  • 2020-12-06 05:16

    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);
    }
    
    0 讨论(0)
  • 2020-12-06 05:17

    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.

    0 讨论(0)
提交回复
热议问题