How to apply filters to *ngFor?
显然,Angular 2将使用管道代替Angular1中的ng-for过滤器来过滤结果,尽管实现起来似乎还很模糊,没有明确的文档。
即我可以从以下角度看待我要实现的目标
1 |
如何使用管道来实现?
基本上,您编写了一个管道,然后可以在
在您的组件中:
1 2 | filterargs = {title: 'hello'}; items = [{title: 'hello world'}, {title: 'hello kitty'}, {title: 'foo bar'}]; |
在模板中,您可以将字符串,数字或对象传递到管道以用于过滤:
1 | <li *ngFor="let item of items | myfilter:filterargs"> |
在您的管道中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'myfilter', pure: false }) export class MyFilterPipe implements PipeTransform { transform(items: any[], filter: Object): any { if (!items || !filter) { return items; } // filter items array, items which match and return true will be // kept, false will be filtered out return items.filter(item => item.title.indexOf(filter.title) !== -1); } } |
记住在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import { MyFilterPipe } from './shared/pipes/my-filter.pipe'; @NgModule({ imports: [ .. ], declarations: [ MyFilterPipe, ], providers: [ .. ], bootstrap: [AppComponent] }) export class AppModule { } |
这是一个Plunker,它演示了如何使用自定义过滤器管道和内置切片管道来限制结果。
请注意(正如一些评论家所指出的那样),Angular中没有内置过滤器管道是有原因的。
你们中的许多人都有很好的方法,但是这里的目标是通用并定义一个数组管道,该数组管道在与* ngFor相关的所有情况下都可以重用。
callback.pipe.ts(不要忘记将其添加到模块的声明数组中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import { PipeTransform, Pipe } from '@angular/core'; @Pipe({ name: 'callback', pure: false }) export class CallbackPipe implements PipeTransform { transform(items: any[], callback: (item: any) => boolean): any { if (!items || !callback) { return items; } return items.filter(item => callback(item)); } } |
然后,在您的组件中,您需要实现一个具有以下符号(项目:any)=>布尔值的方法,例如,在我的例子中,我将其称为filterUser,它可以过滤年龄超过18岁的用户。
您的组件
1 2 3 4 5 6 7 8 | @Component({ .... }) export class UsersComponent { filterUser(user: IUser) { return !user.age >= 18 } } |
最后但并非最不重要的一点,您的html代码将如下所示:
您的HTML
1 2 | <li *ngFor="let user of users | callback: filterUser">{{user.name}} </li> |
如您所见,此Pipe在需要通过回调过滤的所有数组(如项)中是相当通用的。在mycase中,我发现它对于* ngFor这样的场景非常有用。
希望这可以帮助!!!
码矩阵
简化方式(由于性能问题,仅在小型阵列上使用。在大型阵列中,您必须通过代码手动制作过滤器):
参见:https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
1 2 3 4 5 6 7 8 9 10 11 12 | @Pipe({ name: 'filter' }) @Injectable() export class FilterPipe implements PipeTransform { transform(items: any[], field : string, value : string): any[] { if (!items) return []; if (!value || value.length == 0) return items; return items.filter(it => it[field].toLowerCase().indexOf(value.toLowerCase()) !=-1); } } |
用法:
1 2 | <li *ngFor="let it of its | filter : 'name' : 'value or variable'">{{it}} </li> |
如果将变量用作第二个参数,请不要使用引号。
这是我在不使用管道的情况下实现的。
component.html
1 |
component.ts
1 2 3 4 5 6 7 8 9 10 11 12 | @Component({ .... }) export class YourComponent { filter(itemList: yourItemType[]): yourItemType[] { let result: yourItemType[] = []; //your filter logic here ... ... return result; } } |
我不确定它什么时候进来的,但是他们已经制作了可以做到这一点的切片管。它也有据可查。
https://angular.io/docs/ts/latest/api/common/index/SlicePipe-pipe.html
1 2 3 4 | <p *ngFor="let feature of content?.keyFeatures | slice:1:5"> {{ feature.description }} </p> |
您还可以使用以下内容:
1 2 3 | <template ngFor let-item [ngForOf]="itemsList"> </template> |
仅当您的商品符合条件时才显示div
有关更多信息,请参见角度文档。
如果您还需要索引,请使用以下命令:
1 2 3 | <template ngFor let-item [ngForOf]="itemsList" let-i="index"> </template> |
Angular2中的管道类似于命令行中的管道。每个先前值的输出在管道之后被馈送到过滤器中,这使得链接过滤器变得容易,如下所示:
1 2 3 | <template *ngFor="#item of itemsList"> {item | filter1 | filter2} </template> |
为此,我实现并发布了通用组件。看到
https://www.npmjs.com/package/w-ng5
要使用此组件,请先使用npm安装此软件包:
1 | npm install w-ng5 --save |
之后,在app.module中导入模块
1 2 | ... import { PipesModule } from 'w-ng5'; |
在下一步中,添加app.module的声明部分:
1 2 3 4 | imports: [ PipesModule, ... ] |
样品使用
过滤简单字符串
1 2 3 4 5 6 7 8 9 10 11 | <input type="text" [(ngModel)]="filtroString"> <ul> <li *ngFor="let s of getStrings() | filter:filtroString"> {{s}} </li> </ul> |
筛选复杂的字符串-级别2中的字段"值"
1 2 3 4 5 6 7 8 9 10 11 | <input type="text" [(ngModel)]="search"> <ul> <li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.n2.valor2', value: search}]"> {{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}} </li> </ul> |
过滤复杂的字符串-中间字段-级别1中的"值"
1 2 3 4 5 6 7 8 9 10 11 | <input type="text" [(ngModel)]="search3"> <ul> <li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.valor1', value: search3}]"> {{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}} </li> </ul> |
简单过滤复杂数组-字段" Nome"级别0
1 2 3 4 5 6 7 8 9 10 11 | <input type="text" [(ngModel)]="search2"> <ul> <li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'nome', value: search2}]"> {{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}} </li> </ul> |
在树字段中过滤-第2级的" Valor"字段或第1级的" Valor"字段或第0级的" Nome"字段
1 2 3 4 5 6 7 8 9 10 11 | <input type="text" [(ngModel)]="search5"> <ul> <li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.n2.valor2', value: search5}, {field:'n1.valor1', value: search5}, {field:'nome', value: search5}]"> {{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}} </li> </ul> |
过滤不存在??的字段-级别3中不存在" Valor"
1 2 3 4 5 6 7 8 9 10 11 | <input type="text" [(ngModel)]="search4"> <ul> <li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.n2.n3.valor3', value: search4}]"> {{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}} </li> </ul> |
此组件可用于无限属性级别...
我根据这里和其他地方的答案创建了一个小矮人。
另外,我必须添加
Angular2搜索过滤器:PLUNKR(更新:插件不再起作用)
管道将是最好的方法。但低于一个也可以。
1 2 3 | <ng-container *ng-if="conditon(item)"> // my code </ng-container> |
我喜欢用于特定于应用程序的筛选器的另一种方法是在组件上使用自定义只读属性,与使用自定义管道(IMHO)相比,该属性使您可以更清晰地封装筛选逻辑。
例如,如果我想绑定到
1 2 3 4 5 6 7 8 9 10 11 12 13 | searchText:""; albumList: Album[] = []; get filteredAlbumList() { if (this.config.searchText && this.config.searchText.length > 1) { var lsearchText = this.config.searchText.toLowerCase(); return this.albumList.filter((a) => a.Title.toLowerCase().includes(lsearchText) || a.Artist.ArtistName.toLowerCase().includes(lsearchText) ); } return this.albumList; } |
要绑定到HTML中,您可以然后绑定到只读属性:
1 2 | <a class="list-group-item" *ngFor="let album of filteredAlbumList"> |
我发现对于特定于应用程序的专用过滤器,它比管道更好地工作,因为它保持与组件过滤器相关的逻辑。
管道对于全局可重用的过滤器效果更好。
理想情况下,您应该为此创建angualr 2管道。但是您可以做到这一点。
1 2 3 | <ng-container *ngFor="item in itemsList"> {{item}} </ng-container> |
这是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import {Pipe, PipeTransform, Injectable} from '@angular/core'; @Pipe({ name: 'filter' }) @Injectable() export class FilterPipe implements PipeTransform { transform(items: any[], field : string, value): any[] { if (!items) return []; if (!value || value.length === 0) return items; return items.filter(it => it[field] === value); } } |
样品:
1 2 3 4 5 6 | LIST = [{id:1,name:'abc'},{id:2,name:'cba'}]; FilterValue = 1; <span *ngFor="let listItem of LIST | filter : 'id' : FilterValue"> {{listItem .name}} </span> |
与Angular 6一起使用的用于过滤ngFor的简单解决方案如下:
1 2 3 4 5 6 7 | <span *ngFor="item of itemsList" > your code </span |
跨度很有用,因为它并不固有地代表任何东西。
我知道这是一个老问题,但是,我认为提供另一个解决方案可能会有所帮助。
等效于AngularJS
1 |
在Angular 2+中,您不能在同一元素上使用* ngFor和* ngIf,因此它将如下:
1 |
如果不能用作内部容器,请使用ng-container代替。
当您想在应用程序中有条件地附加一组元素(即使用* ngIf =" foo"),但又不想用其他元素包装它们时,ng-container很有用。
我创建了以下管道,用于从列表中获取所需的项目。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'filter' }) export class FilterPipe implements PipeTransform { transform(items: any[], filter: string): any { if(!items || !filter) { return items; } // To search values only of"name" variable of your object(item) //return items.filter(item => item.name.toLowerCase().indexOf(filter.toLowerCase()) !== -1); // To search in values of every variable of your object(item) return items.filter(item => JSON.stringify(item).toLowerCase().indexOf(filter.toLowerCase()) !== -1); } } |
小写转换只是以不区分大小写的方式进行匹配。
您可以像这样在您的视图中使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <input type="text" placeholder="Search reward" [(ngModel)]="searchTerm"> <ul> <li *ngFor="let reward of rewardList | filter:searchTerm"> <img [src]="reward.imageUrl"/> <p> {{reward.name}} </p> </li> </ul> |
这是我创建一段时间并在博客上发布的示例,其中包含一个正在工作的小插曲。它提供了可以过滤任何对象列表的过滤器管道。基本上,您只需在ngFor规范中指定属性和值{key:value}。
它与@NateMay的响应没有太大不同,只是我以相对冗长的细节进行了解释。
就我而言,我使用这种标记过滤了用户输入的一些文本(filterText)的无序列表,该文本是针对我数组中对象的" label"属性的:
1 2 3 4 5 6 7 | <ul> <li *ngFor="let item of _items | filter:{label: filterText}">{{ item.label }} </li> </ul> |
基于上面提出的非常优雅的回调管道解决方案,可以通过允许传递其他过滤器参数来对其进一步概括。然后我们有:
callback.pipe.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'callback', pure: false }) export class CallbackPipe implements PipeTransform { transform(items: any[], callback: (item: any, callbackArgs?: any[]) => boolean, callbackArgs?: any[]): any { if (!items || !callback) { return items; } return items.filter(item => callback(item, callbackArgs)); } } |
零件
1 2 3 4 5 6 | filterSomething(something: Something, filterArgs: any[]) { const firstArg = filterArgs[0]; const secondArg = filterArgs[1]; ... return <some condition based on something, firstArg, secondArg, etc.>; } |
html
1 2 3 4 | <li *ngFor="let s of somethings | callback : filterSomething : [<whatWillBecomeFirstArg>, <whatWillBecomeSecondArg>, ...]"> {{s.aProperty}} </li> |
使用component.ts文件中的
your.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import { Component, Pipe, PipeTransform, Injectable } from '@angular/core'; import { Person} from"yourPath"; @Pipe({ name: 'searchfilter' }) @Injectable() export class SearchFilterPipe implements PipeTransform { transform(items: Person[], value: string): any[] { if (!items || !value) { return items; } console.log("your search token ="+value); return items.filter(e => e.firstName.toLowerCase().includes(value.toLocaleLowerCase())); } } @Component({ .... persons; ngOnInit() { //inicial persons arrays } }) |
和Person对象的数据结构:
人
1 2 3 4 5 6 | export class Person{ constructor( public firstName: string, public lastName: string ) { } } |
在您的html文件视图中:
your.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <input class="form-control" placeholder="Search" id="search" type="text" [(ngModel)]="searchText"/> <table class="table table-striped table-hover"> <colgroup> <col span="1" style="width: 50%;"> <col span="1" style="width: 50%;"> </colgroup> <thead> <tr> <th>First name</th> <th>Last name</th> </tr> </thead> <tbody> <tr *ngFor="let person of persons | searchfilter:searchText"> <td>{{person.firstName}}</td> <td>{{person.lastName}}</td> </tr> </tbody> </table> |
这是你的数组
1 2 3 4 5 6 7 8 9 | products: any = [ { "name":"John-Cena", }, { "name":"Brock-Lensar", } ]; |
这是您的ngFor循环
过滤 :
1 2 3 4 5 6 7 8 9 | <input type="text" [(ngModel)]='filterText' /> <ul *ngFor='let product of filterProduct'> <li> {{product.name }} </li> </ul> |
在那里,我使用产品的filterProduct Instant,因为我想保留原始数据。
这里的_filterText模型用作输入框,任何时候都会调用setter函数。
在setFilterText中,performProduct被调用,它将仅返回与输入匹配的结果。我使用小写字母,不区分大小写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | filterProduct = this.products; _filterText : string; get filterText() : string { return this._filterText; } set filterText(value : string) { this._filterText = value; this.filterProduct = this._filterText ? this.performProduct(this._filterText) : this.products; } performProduct(value : string ) : any { value = value.toLocaleLowerCase(); return this.products.filter(( products : any ) => products.name.toLocaleLowerCase().indexOf(value) !== -1); } |
经过一番谷歌搜索后,我遇到了