ExpressionChangedAfterItHasBeenCheckedError - angular2

开发环境  ‘ExpressionChangedAfterItHasBeenCheckedError’。

在开发环境中我们会遇到上面的这个错误,就是因为angular2的检测机制。

参考文档(本文的很多内容也是翻译或者理解此文而来):

Everything you need to know about the `ExpressionChangedAfterItHasBeenCheckedError` error

问题背景:

我们在用angular2的时候有时候会遇到这个错误,但是如果发布到到生产环境的话,错误就不存在,程序正常运行。

Angular is running in the development mode. Call enableProdMode() to enable the production mode.

这句话是我们在使用angular2的时候控制台常见的提示,当然按照这句话的说法,我们可以在开发环境中使用enableProdMode(),会执行和开发环境的一样的检测机制。

import{enableProdMode }from'@angular/core';

enableProdMode();

这样的话可以最简单的解决这个报错。生产环境去掉这个就可以了。

这当然不是angular2的一个bug,这是一个警戒机制,以防止模型数据和UI之间的不一致,从而不会向页面上的用户显示错误或旧的数据。开发环境和生产环境的检测机制是不一样的,在开发环境,当数据模型和ui不一致的可能就会报这个错误。


angular2的相关变更检测操作

angular2组件生命周期

运行的Angular应用程序是一个组件树。 在更改检测期间,Angular对每个组件执行检查,该组件由按照指定顺序执行的以下操作组成:

       ------更新所有子组件/指令的绑定属性

       ------调用所有子组件/指令的ngOnInit,OnChanges和ngDoCheck生命周期钩子

       ------更新当前组件的DOM

       ------运行子组件的更改检测

       ------调用所有子组件/指令的ngAfterViewInit生命周期钩子

每次操作后,Angular会记住用于执行操作的值。 它们存储在组件视图的oldValues属性中。 对所有组件进行检查后,Angular将开始下一个摘要循环,但不是执行上面列出的操作,而是将当前值与之前摘要循环中记录的值进行比较:

      ------检查传递给子组件的值与现在用于更新这些组件的属性的值是否相同

      ------检查用于更新DOM元素的值是否与现在用于更新这些元素的值是否相同

      ------对所有子组件执行相同的检查

下面也引用文章里的例子,有A组件父组件,B组件子组件,并A组件有text传值到B组件,B组件@Input()接收。

@Component({

      selector: 'a-comp',

      template: `{{name}}`}

)

export class AComponent {

    name = 'I am A component';

    text = 'A message for the child component`;

}

下面是当Angular运行更改检测时会发生什么。 

它首先检查A组件。 列表中的第一个操作是更新绑定,以便将文本表达式计算为子组件的A消息,并将其传递给B组件。 它还将此值存储在视图上:

view.oldValues[0] = 'A message for the child component';

然后Angular执行下一个操作并对子B组件运行相同的检查。 一旦B组件被检查,当前的检测循环就完成了。

如果Angular在开发模式下运行,则运行上面列出的第二个摘要执行验证操作。 现在想象一下,在Angular将子组件的值A消息传递给B组件并将其存储之后,属性文本在A组件上更新为更新的文本。 所以现在运行验证摘要,第一个操作是检查属性文本是否没有更改。

这时候发现改变了就会保持前面的错误。

重现1.当前这个做demo可以在A组件的AfterCheced里改变

ngAfterViewChecked() {

             this.text="new2 - A message for the child component";

}

这时候就会抛出前面的提到的错误。

重现2.另一个我们可以通过在子组件里注入父组件,然后改变父组件的text值,这时候是已经完成了绑定,然后进行的修改,同样也会报错相同的错误,认为你的值有变化, 也就是在绑定存储完之后。

B组件里

import { AComponent } from ‘./aComponent'

export class BComponent {

    @Input() text;

    constructor(private parent: AComponent) {}

    ngAfterViewInit() {

        this.parent.name = 'New';

    }

}

这时候问题就会重现。

当然实际的开发当中的情况不是这么简单的。一般开发的时候的数据可能是通过服务获得。当然不出这个问题更好。出这个问题的话我们就要想到解决方法。

下面我们看一下解决方案:

1.    也是上面提到的。直接添加enableProdMode()

2.    将第二个的重现里改成异步的,加定时器也会发现不报错了。当前实际环境可不是这样的。

ngAfterViewInit() {

      setTimeout( ()=>{

           this.parent.text='New';

     })

}

实际环境没这么简单,具体这种方法怎么应用到实际环境还有待研究。

3. 从第一个重现我们看到在检测完之后改变是会报错的,实际这个问题也就是检测完之后数据发生变化引起的,实际上我们可以通过代码可以让angular2强制执行检测。

    链接文章里说在AfterviewInit里加,不过我是用了没效果,加在AfterViewChecked里起作用的。

import{Component, OnInit, Input, ChangeDetectorRef }  from  '@angular/core';

export class AComponent {

     constructor(private cd: ChangeDetectorRef) {}

     ngOnIOnit(){}

     ngAfterViewChecked() {

          this.cd.detectChanges();

     }

}

4.  这个就根据实际情况来了,有时候这个问题的产生可能是因为自己DOM结构不合理引起的,比如要给子组件传的数据是不是依赖于页面上这个DOM节点之后的元素的绑定的数据。

    换一下位置可能就解决了,当然如果这个位置不能变的话就得在考虑其他方式了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 版本:Angular 5.0.0-alpha AngularDart(本文档中我们通常简称 Angular ) 是...
    soojade阅读 821评论 0 4
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,464评论 25 707
  • 我们一大家子都要在奶奶家过年,不出意外还是曾经的那个家。饭还没有做好,聪提议玩一个游戏。 我先看他们是怎么玩的,看...
    爱梦的我阅读 185评论 0 0
  • 胶东过年,家家要蒸大饽饽。饽饽是发面做的,寓意“大发”。不管是期盼事业蒸蒸日上,还是渴求生意发达,总之,这“发”字...
    烟窑阅读 670评论 2 3