角度2排序和过滤器


77

在Angularjs 1中,可以通过以下方式进行排序和过滤:

<ul ng-repeat="friend in friends | filter:query | orderBy: 'name' ">
   <li>{{friend.name}}</li>
</ul>

但是我在Angularjs 2.0中找不到如何执行此操作的任何示例。我的问题是如何在Angularjs 2.0中进行排序和过滤?如果仍然不支持它,是否有人知道何时或是否将其放入Angularjs 2.0?



谢谢,我将尝试实现自己的过滤器和排序管道。奇怪的是,Angular团队决定删除此功能。希望他们能把它放回去,以便于过滤和分类。
alexsoftware 2015年

您不能只对friends组件代码进行排序吗?
Red Cricket'7

Answers:


81

这不是开箱即用的,因为Angular团队希望Angular 2缩小运行。OrderBy的思考会因缩小而中断。查看MiškoHeverey对此事的回复

我花了一些时间来创建同时支持一维和多维数组的OrderBy管道。它还支持能够对多维数组的多个列进行排序。

<li *ngFor="let person of people | orderBy : ['-lastName', 'age']">{{person.firstName}} {{person.lastName}}, {{person.age}}</li>

此管道确实允许在渲染页面后将更多项目添加到数组,并且仍然正确地对包含新项目的数组进行排序。

在这里写下有关该过程的内容

这是一个工作示例:http : //fuelinteractive.github.io/fuel-ui/#/pipe/orderbyhttps://plnkr.co/edit/DHLVc0?p=info

编辑:向http://fuelinteractive.github.io/fuel-ui/#/pipe/orderby添加了新的演示

编辑2:将ngFor更新为新语法


2
小更新。最新的Angular2 * ngFor将使用* ngFor =“ let person of people ...”代替#号标签
Pat M

2
@CoryShaw很棒的烟斗!做得好****拍手表情符号********
Chaos

1
@invot正确,此管道仅是orderBy功能。应该在阵列上使用第二个管道进行过滤。select的答案是过滤的一个很好的例子
Cory Shaw

2
@ 72GM多维实际上是[][],所以您是正确的。多层次的措词会更好吗?[].SomeObjectProperty.SomeProperty
科里·肖

1
我正在使用Angular 6,并在大约5分钟内使它工作。刚刚使用CLI创建了一个管道,然后复制了代码并修复了TS-Lint展示的所有小问题(只是lint错误),它就像一个魅力一样工作。在开始的一个小时内,它已经在我的应用中使用了5次-很棒的烟斗!
阿尔法·布拉沃

22

设计不支持它。sortBy管道可能会导致生产规模应用程序出现实际性能问题。这是Angular版本1的问题。

您不应创建自定义排序功能。相反,您应该首先在打字稿文件中对数组进行排序,然后再显示它。如果例如在选择下拉菜单时需要更新订单,则使该下拉菜单选择触发一个函数并从中调用您的排序函数。可以将此排序功能提取到服务,以便可以重新使用它。这样,仅在需要时才应用排序,并且您的应用程序性能会更好。


1
虽然我确实了解其背后的原因,但我发现在没有明显替代的情况下松开诸如常见功能之类的内容会感到非常沮丧。当然,我可以实现自己的排序等,但是为什么没有用于服务而不是模板的简单替换方法。
schneida

1
@schneida:也许是因为还有许多其他库可以按照您喜欢的方式为您进行排序,并且您知道并可以使用这些折衷方案?比较即。为什么momentmoment-timezone默认情况下不包含,这是这样一个共同的特点,无明显更换过(顺便说一句看外包装。angular2-moment相应管道的时刻一样的东西-这也说明它是多么容易做一个插件,将填补了国内空白)
羽蛇神

Imho排序和过滤逻辑属于您的存储选择器(如果您使用Redux / RxJS或类似的其他模式)。
Spock

18

这是一个简单的过滤器管道,用于包含具有字符串值(ES6)属性的对象数组

filter-array-pipe.js

import {Pipe} from 'angular2/core';

// # Filter Array of Objects
@Pipe({ name: 'filter' })
export class FilterArrayPipe {
  transform(value, args) {
    if (!args[0]) {
      return value;
    } else if (value) {
      return value.filter(item => {
        for (let key in item) {
          if ((typeof item[key] === 'string' || item[key] instanceof String) && 
              (item[key].indexOf(args[0]) !== -1)) {
            return true;
          }
        }
      });
    }
  }
}

您的组件

myobjComponent.js

import {Component} from 'angular2/core';
import {HTTP_PROVIDERS, Http} from 'angular2/http';
import {FilterArrayPipe} from 'filter-array-pipe';

@Component({
  templateUrl: 'myobj.list.html',
  providers: [HTTP_PROVIDERS],
  pipes: [FilterArrayPipe]
})
export class MyObjList {
  static get parameters() {
    return [[Http]];
  }
  constructor(_http) {
    _http.get('/api/myobj')
      .map(res => res.json())
      .subscribe(
        data => this.myobjs = data,
        err => this.logError(err))
      );
  }
  resetQuery(){
    this.query = '';
  }
}

在您的模板中

myobj.list.html

<input type="text" [(ngModel)]="query" placeholder="... filter" > 
<div (click)="resetQuery()"> <span class="icon-cross"></span> </div>
</div>
<ul><li *ngFor="#myobj of myobjs| filter:query">...<li></ul>

这些文件看起来与打字稿的编写方式非常相似。这些文件实际上是.ts吗?
Alex J

它们不是Typepscript,您必须使用来在函数头transform(value: ..., args: ...)和变量声明中添加类型let
选择

但是当与节点一起使用时,它会在json对象上的filer:query上给出错误。您知道为什么会出现此错误吗?
Jignesh Vagh's

1
@JigneshVaghfilter是一个数组函数,因此它仅适用于对象列表,而不适用于对象本身,希望对您有所帮助。
选择

13

管道将数据作为输入,并将其转换为所需的输出。添加此管道文件:orderby.ts在您的/app文件夹中。

定单

//The pipe class implements the PipeTransform interface's transform method that accepts an input value and an optional array of parameters and returns the transformed value.

import { Pipe,PipeTransform } from "angular2/core";

//We tell Angular that this is a pipe by applying the @Pipe decorator which we import from the core Angular library.

@Pipe({

  //The @Pipe decorator takes an object with a name property whose value is the pipe name that we'll use within a template expression. It must be a valid JavaScript identifier. Our pipe's name is orderby.

  name: "orderby"
})

export class OrderByPipe implements PipeTransform {
  transform(array:Array<any>, args?) {

    // Check if array exists, in this case array contains articles and args is an array that has 1 element : !id

    if(array) {

      // get the first element

      let orderByValue = args[0]
      let byVal = 1

      // check if exclamation point 

      if(orderByValue.charAt(0) == "!") {

        // reverse the array

        byVal = -1
        orderByValue = orderByValue.substring(1)
      }
      console.log("byVal",byVal);
      console.log("orderByValue",orderByValue);

      array.sort((a: any, b: any) => {
        if(a[orderByValue] < b[orderByValue]) {
          return -1*byVal;
        } else if (a[orderByValue] > b[orderByValue]) {
          return 1*byVal;
        } else {
          return 0;
        }
      });
      return array;
    }
    //
  }
}

在组件文件(app.component.ts)中,使用以下命令导入刚添加的管道: import {OrderByPipe} from './orderby';

然后,*ngFor="#article of articles | orderby:'id'"如果要按ID升序或orderby:'!id'"降序对文章进行排序,请在模板中添加。

我们通过在管道名称后加上冒号(:),然后添加参数值,将参数添加到管道

我们必须在@Component装饰器的管道数组中列出管道。pipes: [ OrderByPipe ]

app.component.ts

import {Component, OnInit} from 'angular2/core';
import {OrderByPipe} from './orderby';

@Component({
    selector: 'my-app',
    template: `
      <h2>orderby-pipe by N2B</h2>
      <p *ngFor="#article of articles | orderby:'id'">
        Article title : {{article.title}}
      </p>
    `,
    pipes: [ OrderByPipe ]

})
export class AppComponent{
    articles:Array<any>
    ngOnInit(){
        this.articles = [
        {
            id: 1,
            title: "title1"
        },{
            id: 2,
            title: "title2",
        }]  
    }

}

更多信息在我的github上这篇文章在我的网站上


1
请不要发布重复的答案。相反,请考虑采取其他措施来帮助将来的用户找到所需的答案,如链接文章中所述。特别是当帖子包含指向您网站的链接时,这些帖子开始显得垃圾邮件。
Mogsdad '16

在这种情况下,请阅读如何提供个人开源库?太。
马丁·皮特斯

好的@Mogsdad-对不起,希望它对您有所帮助!
Nicolas2bert '16

4
我喜欢这种结构化的方式,易于理解。谢谢。但是,请在此处和您的网站/ github上更新脚本。在第一行中,将“ angular2 / core”更改为“ @ angular / core”。除此之外,一开始我无法正常工作,然后浏览了您的代码,意识到您的“模板”步骤有很大的错别字。在模板中,它必须是一个数组,所有内容才能按预期工作:“ | orderby:['id']”
Starwave

1
@Starwave +1提及模板中的错误。非常感激。
加里

4

您必须创建自己的Pipe进行数组排序,这是一个示例,您如何做到这一点。

<li *ngFor="#item of array | arraySort:'-date'">{{item.name}} {{item.date | date:'medium' }}</li>

https://plnkr.co/edit/DU6pxr?p=preview


10
他们为什么不开箱即用?这是很普通的事情,他们没有提供,这很荒谬。
bersling 2016年

另外我认为您的实现中存在问题,它可以与'-date'一起使用,但是如果您只想编写'date',则该let column = args[0].slice(1);命令将使该列成为正好ate显然不再是有效的键。
bersling '16

1
@bersling他们的理由是,他们希望Angular 2能够被最小化并且仍然可以正常工作。OrderBy需要无法减少的反思
Cory Shaw

2
那没有道理...上述所有解决方案都可以缩小,对吗?这样一个主要的框架怎么能省略这样的基本功能……
Kokodoko

1

这是我的排序。它将进行数字排序,字符串排序和日期排序。

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

@Pipe({
  name: 'sortPipe'
 })

export class SortPipe implements PipeTransform {

    transform(array: Array<string>, key: string): Array<string> {

        console.log("Entered in pipe*******  "+ key);


        if(key === undefined || key == '' ){
            return array;
        }

        var arr = key.split("-");
        var keyString = arr[0];   // string or column name to sort(name or age or date)
        var sortOrder = arr[1];   // asc or desc order
        var byVal = 1;


        array.sort((a: any, b: any) => {

            if(keyString === 'date' ){

                let left    = Number(new Date(a[keyString]));
                let right   = Number(new Date(b[keyString]));

                return (sortOrder === "asc") ? right - left : left - right;
            }
            else if(keyString === 'name'){

                if(a[keyString] < b[keyString]) {
                    return (sortOrder === "asc" ) ? -1*byVal : 1*byVal;
                } else if (a[keyString] > b[keyString]) {
                    return (sortOrder === "asc" ) ? 1*byVal : -1*byVal;
                } else {
                    return 0;
                }  
            }
            else if(keyString === 'age'){
                return (sortOrder === "asc") ? a[keyString] - b[keyString] : b[keyString] - a[keyString];
            }

        });

        return array;

  }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.