选中后错误值已更改(Angular2中的Stateful Pipe)

时间:2015-12-27 00:19:30

标签: angular

我有一个简单的管道,可以过滤一系列学生。这是代码(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]

为什么?

1 个答案:

答案 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会在开发模式下检查绑定两次,并在检测到更改时引发运行时错误。

Updated Plunkr