【问题标题】:Error value has changed after checked (Stateful Pipe in Angular2)检查后错误值已更改(Angular2中的状态管道)
【发布时间】:2016-04-01 07:09:28
【问题描述】:

我有一个过滤学生数组的简单管道。这是代码 (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


【解决方案1】:

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

Updated Plunkr

【讨论】:

  • 仅供参考,您可以使用$this.temp.push(...tmp),而不是tmp.forEach( )。 -- Default + Rest + Spread
  • 我试图了解变更检测的工作原理。您说“AngularJS 必须在每个摘要周期执行管道,并且至少执行两次,直到输出稳定。”你有这方面的参考,所以我可以了解更多信息吗?我正在阅读Savkin's blog,在评论中(在对用户 Foo 的回复中)他说“在 Angular2 中,我们只遍历一次更改检测图。”我似乎无法理解这个陈述和你的陈述——它们似乎有冲突,但也许我遗漏了一些东西。
  • 我想我找到了答案。在this answer by @drewmoore 中,他指出“简而言之,在开发模式下,每轮更改检测之后都会立即进行第二轮,验证自第一轮结束以来没有任何绑定发生更改,因为这表明正在发生更改通过变化检测本身。”如果我在生产模式下运行@ChuSon 的“这不起作用”代码,则不会出现错误,并且确实有效。 plunker
  • 我认为你是对的。我已经对 Angular2 如何进行更改检测进行了一些进一步的研究,但我不相信我的答案是正确的(它实际上可能只适用于 Angular 1.x)。我会尝试修改这个答案。
  • ApplicationRef.tick() docs 讨论了开发模式下第二个更改的检测周期。在此处的场景(有状态管道)中,我认为该错误是虚假/误导性的。我们有一个有状态的管道,每次调用时输出都会改变/产生副作用——它是有状态的,没关系。 NgFor 在管道之后进行评估,因此它应该可以正常工作。但由于我们无法在抛出此错误的情况下进行开发,因此保留数组引用是一种解决方法。
猜你喜欢
  • 1970-01-01
  • 2016-06-13
  • 2017-06-08
  • 2017-12-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-26
  • 2016-04-15
相关资源
最近更新 更多