可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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>
回答2:
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);
}
回答3:
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>
回答4:
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.
回答5:
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.
回答6:
I found the simplest solution to be the following:
- In your component: add a field that will hold the current count
filterMetadata = { count: 0 };
- 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">
- interpolate filterMetadata.count into the element that displays the number of records displayed.
<span> {{filterMetadata.count}} records displayed</span>
- 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.
回答7:
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.