我有一个简单的管道,可以过滤一系列学生.这是代码(Plnkr)
import {Pipe} from 'angular2/core'; @Pipe({ name: 'sortByName', pure: false }) export class SortByNamePipe { temp = []; // i = 0; transform (value, [queryString]) { // console.log(this.i++); // console.log(value, queryString); // This does not work this.temp = value.filter((student)=>(student)=>student.name.includes(queryString))) return value.map(function(val){ return val.name.toUpperCase()}); // This works // this.temp.length = 0; // this.temp.push(...value.filter((student)=>student.name.includes(queryString))) // return this.temp; } }
正如您在Plnkr中看到的,Angular使用第一种方法抛出错误.
EXCEPTION: Expression 'students | sortByName:queryElem.value in HelloWorld@7:6' has changed after it was checked. Previous value: 'SON,DAVID'. Current value: 'SON,DAVID' in [students | sortByName:queryElem.value in HelloWorld@7:6]
为什么?
Angular无法对有状态管道进行某些优化,也无法对无状态(或纯粹)管道进行某些优化.例如,如果管道是无状态的,那么过滤器的输出仅取决于其输入(left | pipe:args).只要'left'或'args'没有改变,那么输出就不会改变.这允许AngularJS在输入未更改时安全地跳过管道的执行.
对于有状态管道,即使对于相同的输入,管道的输出也可以改变.
在第一轮更改检测后检查后,错误告诉您数组引用已更改:
... has changed after it was checked. Previous value: 'SON,DAVID'. Current value: 'SON,DAVID'...
我修改了你的第一个例子以保留数组引用:
// This now works var $this = this; // save this $this.temp.length = 0; var tmp = value.filter((student)=>student.name.includes(queryString)); tmp.forEach(function (val) {$this.temp.push(val);}); return $this.temp;
[编辑]
正如Mark指出的那样,错误只发生在开发模式中.如果更改为生产模式,则错误消失,代码按预期工作.
[说明]
显然,在开发模式下,angular会检查你的绑定两次,以确保它们没有改变.
https://github.com/angular/angular/issues/6006
https://github.com/angular/angular/issues/6005
问题是当第一轮变化检测后绑定发生变化时,它不会触发新一轮的变化检测.这是不合需要的,因为在未来的一轮变化检测之前不会更新绑定.为确保不会发生这种情况,Angular会在开发模式下检查绑定两次,并在检测到更改时引发运行时错误.
更新了Plunkr