应用管道后如何在 ngFor 中查找项目数
How to find the count of items in an ngFor after the pipes have been applied
我有一个 ngFor 在 table 中创建行,它被过滤和分页。
<tr *ngFor="#d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">
页面上还有另一个元素显示显示的记录数。这些元素最初绑定到 data.Results' 长度。
如何获取应用过滤管道后显示的数据长度,以便正确显示。 None 在 ngFor 中提供的局部变量似乎可以解释这一点。
一种方法是使用模板变量 @ViewChildren()
<tr #myVar *ngFor="let d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">
@ViewChildren('myVar') createdItems;
ngAfterViewInit() {
console.log(this.createdItems.toArray().length);
}
您可以通过在管道中转换数组来获取项目的计数。
想法是,管道会将数组转换为另一个数组,其中每个元素都有一个项目 属性,以及一个表示过滤后(或原始)数组的父级 属性:
@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>
这不完全是原始问题的目的,但我也在寻找一种方法来在应用所有管道后显示项目数。通过结合 ngFor 提供的索引和最后一个值,我找到了另一个解决方案:
<div *ngFor="#item of (items | filter); #i = index; #last = last">
...
<div id="counter_id" *ngIf="last">{{index + 1}} results</div>
</div>
所以我找到了解决方法。
我创建了一个管道,它采用对象引用并使用当前通过管道的计数更新 属性。
@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;
}
}
}
然后在你的视图中link像这样添加一个分页组件。
pagination-controls(#controls="", [items]="items.length", (onChange)="o")
tbody
tr(*ngFor=`let item of items
| filter_pipe: { .... }
| count: { object: controls , key: 'items' }
| pagination_pipe: { ... } `)
一旦计数 属性 从管道中提取到当前组件或子组件,您就可以用它做任何事情。
我遇到了同样的问题,虽然@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
,否则我建议你在上面的例子中避免使用它们。
在我的例子中,我需要 运行 通过过滤元素和 运行 一些分析。
我的解决方案是简单地传入一个函数和 return 最后一个管道上的数组。您也可以专门为此创建一个管道,但我已将其添加到过滤器中的最后一个管道中:
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);
现在可以在控制器中处理了。
我发现最简单的解决方案如下:
- 在您的组件中:添加一个用于保存当前计数的字段
filterMetadata = { count: 0 };
- 在您的模板中:将 filterMetadata 作为参数添加到过滤器管道
<tr *ngFor="#d of data.results | filter:filterText:filterMetadata | pagination:resultsPerPage:currentPage">
- 将 filterMetadata.count 插入到显示显示记录数的元素中。
<span> {{filterMetadata.count}} records displayed</span>
- 在过滤器管道中,完成过滤后更新 filterMetadata.count 字段
transform(items, ..., filterMetadata) {
// filtering
let filteredItems = filter(items);
filterMetadata.count = filteredItems.length;
return filteredItems;
}
此解决方案仍然使用纯管道,因此没有性能下降。
如果您有多个进行过滤的管道,则应将 filterMetadata 作为参数添加到所有管道中,因为一旦管道 returns 为空数组,angular 就会停止调用管道序列,因此您可以'只需将其添加到第一个或最后一个过滤管道即可。
假设您的 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}}
优点包括不需要修改管道以 return 附加数据。
对我有用的是:
不要使用管道,几个月后你将无法分辨它们的含义,也无法理解奇怪的语法。
Frameworks,这里 Angular,没问题,但在某种程度上,保持模板简单 ngFor 绑定到数据数组。超越这意味着您将纠结于特定框架的特殊语法和(更改)机制。 (这解释了为什么我们有这个 post/question 首先不应该存在)
HTML 模板用于布局,保持原样。所有的逻辑、数据过滤等...都应该直接放在后面的代码中 类.
只需在您的组件或服务中创建一个过滤方法并调用它来过滤您的数据。
- 在您的 component/service 上公开一个 .Count 道具以显示您的实际过滤数据动态计数(即通常为 .length)。
我有一个 ngFor 在 table 中创建行,它被过滤和分页。
<tr *ngFor="#d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">
页面上还有另一个元素显示显示的记录数。这些元素最初绑定到 data.Results' 长度。
如何获取应用过滤管道后显示的数据长度,以便正确显示。 None 在 ngFor 中提供的局部变量似乎可以解释这一点。
一种方法是使用模板变量 @ViewChildren()
<tr #myVar *ngFor="let d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">
@ViewChildren('myVar') createdItems;
ngAfterViewInit() {
console.log(this.createdItems.toArray().length);
}
您可以通过在管道中转换数组来获取项目的计数。
想法是,管道会将数组转换为另一个数组,其中每个元素都有一个项目 属性,以及一个表示过滤后(或原始)数组的父级 属性:
@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>
这不完全是原始问题的目的,但我也在寻找一种方法来在应用所有管道后显示项目数。通过结合 ngFor 提供的索引和最后一个值,我找到了另一个解决方案:
<div *ngFor="#item of (items | filter); #i = index; #last = last">
...
<div id="counter_id" *ngIf="last">{{index + 1}} results</div>
</div>
所以我找到了解决方法。
我创建了一个管道,它采用对象引用并使用当前通过管道的计数更新 属性。
@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;
}
}
}
然后在你的视图中link像这样添加一个分页组件。
pagination-controls(#controls="", [items]="items.length", (onChange)="o")
tbody
tr(*ngFor=`let item of items
| filter_pipe: { .... }
| count: { object: controls , key: 'items' }
| pagination_pipe: { ... } `)
一旦计数 属性 从管道中提取到当前组件或子组件,您就可以用它做任何事情。
我遇到了同样的问题,虽然@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
,否则我建议你在上面的例子中避免使用它们。
在我的例子中,我需要 运行 通过过滤元素和 运行 一些分析。
我的解决方案是简单地传入一个函数和 return 最后一个管道上的数组。您也可以专门为此创建一个管道,但我已将其添加到过滤器中的最后一个管道中:
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);
现在可以在控制器中处理了。
我发现最简单的解决方案如下:
- 在您的组件中:添加一个用于保存当前计数的字段
filterMetadata = { count: 0 };
- 在您的模板中:将 filterMetadata 作为参数添加到过滤器管道
<tr *ngFor="#d of data.results | filter:filterText:filterMetadata | pagination:resultsPerPage:currentPage">
- 将 filterMetadata.count 插入到显示显示记录数的元素中。
<span> {{filterMetadata.count}} records displayed</span>
- 在过滤器管道中,完成过滤后更新 filterMetadata.count 字段
transform(items, ..., filterMetadata) {
// filtering
let filteredItems = filter(items);
filterMetadata.count = filteredItems.length;
return filteredItems;
}
此解决方案仍然使用纯管道,因此没有性能下降。 如果您有多个进行过滤的管道,则应将 filterMetadata 作为参数添加到所有管道中,因为一旦管道 returns 为空数组,angular 就会停止调用管道序列,因此您可以'只需将其添加到第一个或最后一个过滤管道即可。
假设您的 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}}
优点包括不需要修改管道以 return 附加数据。
对我有用的是:
不要使用管道,几个月后你将无法分辨它们的含义,也无法理解奇怪的语法。
Frameworks,这里 Angular,没问题,但在某种程度上,保持模板简单 ngFor 绑定到数据数组。超越这意味着您将纠结于特定框架的特殊语法和(更改)机制。 (这解释了为什么我们有这个 post/question 首先不应该存在)
HTML 模板用于布局,保持原样。所有的逻辑、数据过滤等...都应该直接放在后面的代码中 类.
只需在您的组件或服务中创建一个过滤方法并调用它来过滤您的数据。
- 在您的 component/service 上公开一个 .Count 道具以显示您的实际过滤数据动态计数(即通常为 .length)。