如何将过滤器应用于*ngFor?

Kha*_*led 237 typescript angular

显然,Angular 2将使用管道而不是Angular1中的过滤器以及ng-for来过滤结果,尽管实现似乎仍然模糊,没有明确的文档.

也就是说,我想要实现的目标可以从以下角度来看待

<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
Run Code Online (Sandbox Code Playgroud)

如何使用管道实现?

phu*_*c77 342

基本上,你编写一个管道然后可以在*ngFor指令中使用.

在您的组件中:

filterargs = {title: 'hello'};
items = [{title: 'hello world'}, {title: 'hello kitty'}, {title: 'foo bar'}];
Run Code Online (Sandbox Code Playgroud)

在模板中,您可以将字符串,数字或对象传递给管道以用于过滤:

<li *ngFor="let item of items | myfilter:filterargs">
Run Code Online (Sandbox Code Playgroud)

在你的管道中:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

记得注册你的管道app.module.ts; 你不再需要在你的管道中注册管道了@Component

import { MyFilterPipe } from './shared/pipes/my-filter.pipe';

@NgModule({
    imports: [
        ..
    ],
    declarations: [
        MyFilterPipe,
    ],
    providers: [
        ..
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)

这是一个Plunker,它演示了使用自定义过滤器管道和内置切片管道来限制结果.

请注意(正如几位评论员指出的那样)Angular中没有内置过滤管道的原因.

  • Thanx,这项工作按预期进行,但有时最好检查items数组是否已定义且不为null,因为Ng2可能会尝试应用过滤器,而"items"仍未定义. (6认同)
  • Angular 说使用 Pipe 有执行问题,所以建议对组件进行过滤 (3认同)
  • 我想建议在括号中包装`* ngFor`参数,以避免任何混淆并使它“防更改”:`&lt;li * ngFor =“让(items | myfilter:filterargs)”&gt;项目 (2认同)

cod*_*de5 86

很多人都有很好的方法,但这里的目标是通用并定义一个数组管道,它在与*ngFor的关系中可以非常重复使用.

callback.pipe.ts(不要忘记将它添加到模块的声明数组中)

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的组件中,你需要实现一个具有以下signuature的方法(item:any)=> boolean,在我的例子中,我称之为filterUser,它过滤用户的年龄大于18岁.

你的组件

@Component({
  ....
})
export class UsersComponent {
  filterUser(user: IUser) {
    return !user.age >= 18
  }
}
Run Code Online (Sandbox Code Playgroud)

最后但并非最不重要的是,您的HTML代码将如下所示:

你的HTML

<li *ngFor="let user of users | callback: filterUser">{{user.name}}</li>
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,此管道在所有数组中都非常通用,例如需要通过回调过滤的项目.在我的案例中,我发现它对*ngFor类似场景非常有用.

希望这可以帮助!!!

codematrix

  • 为了避免"this"未定义的问题,你可以在你的组件上编写你的方法,比如`filterUser =(user:IUser)=>`而不是`filteruser(user:IUser)` (5认同)
  • 我注意到在函数filterUser()中 - 或者我的等效函数 - 你不能像使用组件类中的所有其他函数那样使用"this"来访问当前组件实例.我需要访问组件对象以检查过滤后的项目是否在集合中. (3认同)
  • @Paul我知道这已经太晚了,无法帮助你,但它可能会帮助其他人。您在组件方法上丢失“this”的原因是因为该方法被用作回调并且应用了新的“this”上下文。您遇到了面向对象 javascript 中的一个常见问题,但有一个古老而简单的解决方案:将用作原始类回调的方法绑定起来。在构造函数中,添加以下代码:`this.myCallbackFunc = this.myCallbackFunc.bind(this);` 就是这样。你永远不会再失去“这个”。 (3认同)

Rod*_*ira 33

简化方式(由于性能问题仅在小型阵列上使用.在大型阵列中,您必须通过代码手动生成过滤器):

请参阅:https://angular.io/docs/ts/latest/guide/pipes.html#!#no-filter-pipe

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

<li *ngFor="let it of its | filter : 'name' : 'value or variable'">{{it}}</li>
Run Code Online (Sandbox Code Playgroud)

如果使用变量作为第二个参数,请不要使用引号.

  • 根据Angular团队的说法,这被认为是不好的做法. (5认同)
  • 可能添加以下内容以显示如何将其与ReqExp结合使用:return items.filter(item => {return new RegExp(value,"i").test(item [field])}); (3认同)
  • 根据Angular团队的说法,这是一个糟糕的代码,因为它很慢并且没有很好地缩小.由于他们的代码,团队不希望看到慢速网站,所以他们这次没有将它构建到Angular中.https://angular.io/docs/ts/latest/guide/pipes.html#!#appendix-no-filterpipe-or-orderbypipe- (2认同)

Tha*_* Le 29

这是我在不使用管道的情况下实现的.

component.html

<div *ngFor="let item of filter(itemsList)">
Run Code Online (Sandbox Code Playgroud)

component.ts

@Component({
....
})
export class YourComponent {
  filter(itemList: yourItemType[]): yourItemType[] {
    let result: yourItemType[] = [];
    //your filter logic here
    ...
    ...
    return result;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为这将是计算密集型的,因为Angular每次运行变化检测时都会执行过滤器.它不能很好地扩展到大型阵列.一个更干净但代码更复杂的解决方案是使`itemList`成为一个Observable并使用异步过滤器:`let item of itemsList | async`.发生更改时,使observable发出新列表.这样,过滤代码仅在需要时运行. (11认同)
  • 这个答案应该有负分。不好,用管子吧。 (2认同)
  • 我不确定我是否理解为什么这太糟糕了,无论您使用什么,在更改检测期间管道或其他任何东西都不会潜在地被过滤掉吗?如果你在管道中放置一个断点,你会看到它仍然在每次更改检测时运行。可观察方法比 trackBy 更好,因为在一天结束时,它仍然需要根据值进行过滤一个可能已经改变的变量..?您可以使用单独的列表并更新,然后也推送更改。 (2认同)

Spa*_*ers 16

我不确定它什么时候进来,但是他们已经制作了切片管来做到这一点.它也有很好的记录.

https://angular.io/docs/ts/latest/api/common/index/SlicePipe-pipe.html

<p *ngFor="let feature of content?.keyFeatures | slice:1:5">
   {{ feature.description }}
</p>
Run Code Online (Sandbox Code Playgroud)

  • 如果使用[trackBy接口](https://angular.io/docs/ts/latest/api/core/index/TrackByFn-interface.html),切片管道必须在`;`之前应用.例如:`*ngFor ="让内容的特征?.keyFeatures |切片:1:5; trackBy特征?.id"` (4认同)

Jer*_*oen 12

您还可以使用以下内容:

<template ngFor let-item [ngForOf]="itemsList">
    <div *ng-if="conditon(item)"></div>
</template>
Run Code Online (Sandbox Code Playgroud)

如果您的商品符合条件,则仅显示div

有关详细信息,请参阅角度文档.如果还需要索引,请使用以下命令:

<template ngFor let-item [ngForOf]="itemsList" let-i="index">
    <div *ng-if="conditon(item, i)"></div>
</template>
Run Code Online (Sandbox Code Playgroud)


小智 9

我用的是动态过滤管

来源数据:

items = [{foo: 'hello world'}, {foo: 'lorem ipsum'}, {foo: 'foo bar'}];
Run Code Online (Sandbox Code Playgroud)

在模板中,您可以在任何对象属性中动态设置过滤器:

<li *ngFor="let item of items | filter:{foo:'bar'}">
Run Code Online (Sandbox Code Playgroud)

管道:

  import { Pipe, PipeTransform } from '@angular/core';

  @Pipe({
    name: 'filter',
  })
  export class FilterPipe implements PipeTransform {
    transform(items: any[], filter: Record<string, any>): any {
      if (!items || !filter) {
        return items;
      }

      const key = Object.keys(filter)[0];
      const value = filter[key];

      return items.filter((e) => e[key].indexOf(value) !== -1);
    }
  }
Run Code Online (Sandbox Code Playgroud)

app.module.ts不要忘记在声明中注册管道


Ben*_*ser 8

Angular2中的管道与命令行上的管道类似.每个先前值的输出都被送入管道之后的过滤器,这样就可以很容易地链接过滤器,如下所示:

<template *ngFor="#item of itemsList">
    <div *ngIf="conditon(item)">{item | filter1 | filter2}</div>
</template>
Run Code Online (Sandbox Code Playgroud)

  • 不支持同一元素上的`*ngFor`和`*ngIf`.你需要改变其中一个`<template ngFor ...>`的显式形式 (12认同)

Mic*_*l V 8

一个与 Angular 6 一起用于过滤 ngFor 的简单解决方案,如下所示:

<span *ngFor="item of itemsList"  >
  <div *ngIf="yourCondition(item)">
    
    your code
    
  </div>
</span>
Run Code Online (Sandbox Code Playgroud)

跨度很有用,因为它本质上不代表任何东西。

  • 比 &lt;span&gt; 更好的是使用 &lt;ng-container&gt; 因为它不会添加任何不必要的标记,除了 html 噪音之外还可能影响您的 CSS。 (3认同)

小智 7

我知道这是一个老问题,但是,我认为提供另一种解决方案可能会有所帮助。

相当于这个的 AngularJS

<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
Run Code Online (Sandbox Code Playgroud)

在 Angular 2+ 中,您不能在同一个元素上使用 *ngFor 和 *ngIf,因此它将如下所示:

<div *ngFor="let item of itemsList">
     <div *ngIf="conditon(item)">
     </div>
</div>
Run Code Online (Sandbox Code Playgroud)

如果您不能用作内部容器,请改用 ng-container。当你想在你的应用程序中有条件地附加一组元素(即使用 *ngIf="foo")但不想用另一个元素包装它们时,ng-container 很有用。


Har*_*tel 5

管道将是最好的方法。但下面的一个也可以。

<div *ng-for="#item of itemsList">
  <ng-container *ng-if="conditon(item)">
    // my code
  </ng-container>
</div>
Run Code Online (Sandbox Code Playgroud)


小智 5

对于这个需求,我实现并发布了一个通用组件。看

https://www.npmjs.com/package/w-ng5

要使用此组件,请先使用 npm 安装此包:

npm install w-ng5 --save
Run Code Online (Sandbox Code Playgroud)

之后,在 app.module 中导入模块

...
import { PipesModule } from 'w-ng5';
Run Code Online (Sandbox Code Playgroud)

在下一步中,在 app.module 的声明部分添加:

imports: [
  PipesModule,
  ...
]
Run Code Online (Sandbox Code Playgroud)

样品使用

过滤简单字符串

<input type="text"  [(ngModel)]="filtroString">
<ul>
  <li *ngFor="let s of getStrings() | filter:filtroString">
    {{s}}
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)

过滤复杂字符串 - 级别 2 中的字段“值”

<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>
Run Code Online (Sandbox Code Playgroud)

过滤复杂字符串 - 中间字段 - 级别 1 中的“值”

<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>
Run Code Online (Sandbox Code Playgroud)

过滤复杂数组简单 - 字段 'Nome' 级别 0

<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>
Run Code Online (Sandbox Code Playgroud)

在树字段中过滤 - 级别 2 中的字段“Valor”或级别 1 中的“Valor”或级别 0 中的“Nome”

<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>
Run Code Online (Sandbox Code Playgroud)

过滤不存在​​的字段 - 不存在级别 3 中的“Valor”

<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>
Run Code Online (Sandbox Code Playgroud)

该组件适用于无限属性级别...