我有一个简单的管道,可以过滤一系列学生。这是代码(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]
为什么?
答案 0 :(得分:8)
Angular无法对有状态管道进行某些优化,而无法对无状态(或纯粹)管道进行某些优化。例如,如果管道是无状态的,那么过滤器的输出仅取决于其输入(左 |管道: args )。只要'离开'或者' 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会在开发模式下检查绑定两次,并在检测到更改时引发运行时错误。