【问题标题】:How to find the count of items in an ngFor after the pipes have been applied应用管道后如何查找 ngFor 中的项目数
【发布时间】:2016-07-04 13:48:50
【问题描述】:

我有一个 ngFor 在过滤和分页的表中创建行。

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

页面上有另一个元素显示显示的记录数。这些元素最初绑定到 data.Results 的长度。

如何获取应用过滤管道后显示的数据长度,以便正确显示。 ngFor 中提供的局部变量似乎都没有解释这一点。

【问题讨论】:

  • 所以我在这里写了一个答案,但我认为它不会像我想的那样工作。所以我会把它放在评论中,希望比我更聪明的人可以帮助你!我在想你可以在这里jilles.me/ng-filter-in-angular2-pipes 做这样的事情,然后在你的构造函数中观察onChange event,然后过滤data.results 并在 filterText 更改时对其进行计数。你基本上会过滤两次,这看起来真的很hacky。

标签: angular angular2-directives


【解决方案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);
}

【讨论】:

  • 这非常接近我的需要,谢谢。不幸的是,由于分页延迟加载数据,我总是只得到第一页的 .length。
  • 使用这种方法,您只能获取在 DOM 中实际创建的元素。那时@pixelbits 方法可能更合适。
  • 还必须使用 ngAfterViewChecked() { this.cdRef.detectChanges(); }
【解决方案2】:

我发现最简单的解决方案如下:

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

此解决方案仍然使用纯管道,因此没有性能下降。 如果您有多个管道进行过滤,则应将 filterMetadata 作为参数添加到所有管道,因为一旦管道返回空数组,角度就会停止调用管道序列,因此您不能只将其添加到第一个或最后一个过滤管道。

【讨论】:

  • 这是我使用 Angular 8 的唯一方法。非常好的和干净的解决方案。
【解决方案3】:

您可以通过在管道中转换数组来获取项目的数量。

这个想法是管道会将数组转换为另一个数组,其中每个元素都有一个 item 属性,以及一个表示过滤(或原始)数组的父属性:

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

【讨论】:

  • 我看到了插值 Count 字符串中的数据,太棒了!如何在组件的 .ts 文件中获取这些数据以与其他控件一起使用?我需要让它超出 *ngFor 的范围。
  • 我尝试使用这样的解决方案 &lt;span&gt;{{setLocalVariable(d.parent.length)}}&lt;/span&gt; from here 将 .parent.length 提升到 ngFor 的范围之外。这种方法有效,但 setLocalVariable 函数在循环中被无限调用,即使渲染应该已经完成​​。似乎没有发生任何更改检测,所以我不确定为什么 ngFor 似乎永远在执行。
  • @ghawkes 我认为这种方法不可能。我希望 Angular 添加一些基于过滤定义本地模板变量的方法(类似于索引,但自定义)。也是一种指定它在迭代范围之外可用的方法。这将是一个非常有用的功能,可以处理 ngFor 的一些常见用例。
  • @pixelbits 希望有人在 angular github 上创建了一个功能请求问题。这是一个非常需要的功能。
  • 如果您需要将其放入 component.ts 文件中,此解决方案将无济于事。
【解决方案4】:

这不完全是原始问题的目的,但我也在寻找一种在应用所有管道后显示项目计数的方法。通过结合 ngFor 提供的索引和最后一个值,我找到了另一个解决方案:

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

【讨论】:

  • 在 Angular 4 中,以下内容对我有用: index as i;最后一个
【解决方案5】:

我遇到了同样的问题,虽然@bixelbits 的回答被批准了,但我觉得它并不理想,特别是对于大数据。

与其在每个元素中返回原始数组,我认为最好避免 Pipes 解决这个问题,至少对于当前的 Angular 2 实现 (rc4)。

更好的解决方案是使用普通组件的功能来过滤数据,如下所示:

// 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,否则我建议您在上述示例的情况下避免使用它们。

【讨论】:

    【解决方案6】:

    所以我找到了解决方法。

    我创建了一个管道,它接受一个对象引用并使用当前通过管道的计数更新一个属性。

    @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 属性提取到当前组件或子组件,您就可以对它进行任何操作。

    【讨论】:

      【解决方案7】:

      就我而言,我需要遍历过滤后的元素并进行一些分析。

      我的解决方案是简单地传入一个函数并返回最后一个管道上的数组。您也可以专门为此创建一个管道,但我已将其添加到过滤器中的最后一个管道:

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

      现在可以在控制器中处理它。

      【讨论】:

        【解决方案8】:

        假设您的 ngFor 看起来像:

        <div #cardHolder>
            <app-card *ngFor="let card of cards|pipeA:paramX|pipeB:paramY"></app-card>
        </div>
        

        然后在你的组件中你可以使用类似的东西:

         get displayedCards() : number {
            let ch = this.cardHolder.nativeElement;
        
            // In case cardHolder has not been rendered yet...
            if(!ch)
              return 0;
        
            return ch.children.length;
        
          }
        

        你可以通过简单的插值显示在你的视图中

        {{displayedCards}}
        

        优点包括无需修改管道即可返回额外数据。

        【讨论】:

          【解决方案9】:
          1. 对我有用的是:

            • 不要使用管道,几个月后您将无法分辨它们的含义,也无法弄清楚奇怪的语法。

            • 框架,这里是 Angular,没问题,但在一定程度上,保持模板简单 ngFor 绑定到数据数组。超越这意味着您将纠结于特定框架的特殊语法和(变化的)机制。 (这解释了为什么我们有这个最初不应该存在的帖子/问题)

            • HTML 模板用于布局保持原样。所有逻辑、数据过滤等...都应该保存在代码后面的简单类中。

          2. 只需在您的组件或服务中创建一个过滤器方法并调用它来过滤您的数据。

          3. 在您的组件/服务上公开一个 .Count 道具以显示您实际过滤的数据动态计数(即通常为 .length)。

          【讨论】:

            猜你喜欢
            • 2019-05-15
            • 2017-09-09
            • 2019-08-31
            • 1970-01-01
            • 1970-01-01
            • 2017-03-13
            • 2018-05-04
            • 2022-11-21
            • 2018-04-06
            相关资源
            最近更新 更多