当前位置:  开发笔记 > 编程语言 > 正文

表达式___在检查后发生了变化

如何解决《表达式___在检查后发生了变化》经验,为你挑选了8个好方法。

为什么这个组件在这个简单的插件中

@Component({
  selector: 'my-app',
  template: `
I'm {{message}}
`, }) export class App { message:string = 'loading :('; ngAfterViewInit() { this.updateMessage(); } updateMessage(){ this.message = 'all done loading :)' } }

投掷:

例外:App @ 0:5'中的表达式'我'{{message}}在检查后发生了变化.上一个值:'我正在加载:('.当前值:'我已经完成了加载:)'在[我是{{message}}在App @ 0:5中]

什么时候我正在做的是在我的视图启动时更新一个简单的绑定?



1> Tycho..:

正如drewmoore所述,在这种情况下,正确的解决方案是手动触发当前组件的变化检测.这是使用对象的detectChanges()方法ChangeDetectorRef(从中导入angular2/core)或其markForCheck()方法完成的,该方法还使任何父组件更新.相关例子:

import { Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `
I'm {{message}}
`, }) export class App implements AfterViewInit { message: string = 'loading :('; constructor(private cdr: ChangeDetectorRef) {} ngAfterViewInit() { this.message = 'all done loading :)' this.cdr.detectChanges(); } }

这里同时Plunkers展示ngOnInit,setTimeout的,并且enableProdMode以防万一的方法.


在我的情况下,我打开了一个模态.打开模态后,它显示消息"表达式___在检查后发生了变化",所以我的解决方案添加了this.cdr.detectChanges(); 打开我的模态后.谢谢!
这对我有所帮助-进行了一些修改。我必须设置通过ngFor循环生成的li元素的样式。我需要在单击“ sort”后根据innerText更改列表项中跨度的颜色,这会更新一个用作排序管道参数的布尔值(排序后的结果是数据的副本,因此样式无法获取仅使用ngStyle更新)。-我没有使用'AfterViewInit',而是使用**'AfterViewChecked'**-我还确保了* import *和* implement * AfterViewChecked。*注意:将管道设置为'pure:false'无效,我必须添加此额外步骤(:*

2> drew moore..:

首先,请注意,只有当您以开发模式运行应用程序时才会抛出此异常(默认情况下属于beta-0):如果您enableProdMode()在引导应用程序时调用它,则不会抛出它(请参阅更新了plunk).

其次,不要这样做是因为这个例外被抛出是有充分理由的:简而言之,当处于开发模式时,每一轮变更检测都会立即进行第二轮检查,以确认自第一轮结束后没有任何绑定发生变化,因为这表明变化是由变化检测本身引起的.

在你的插件中,绑定{{message}}会被你的调用改变setMessage(),这发生在ngAfterViewInit钩子中,这是初始变化检测转弯的一部分.这本身并不成问题 - 问题在于setMessage()更改绑定但不会触发新一轮的更改检测,这意味着直到某个未来的一轮更改检测在其他地方触发时才会检测到此更改.

要点:任何改变绑定的东西都需要触发一轮变化检测.

更新以响应所有请求以获取如何执行此操作的示例:@Tycho 的解决方案正常工作,@ MarkRajcok指出的答案中的三种方法也是如此.但坦率地说,他们都觉得我很难看,就像我们习惯于在ng1中依赖的那种黑客一样.

可以肯定的是,偶尔存在这些黑客攻击的情况,但是如果你在非常偶然的基础上使用它们,那就表明你正在与框架作斗争,而不是完全接受它的反应性质.

恕我直言,一个更惯用的,"Angular2方式"接近这一点是这样的:( plunk)

@Component({
  selector: 'my-app',
  template: `
I'm {{message | async}}
` }) export class App { message:Subject = new BehaviorSubject('loading :('); ngAfterViewInit() { this.message.next('all done loading :)') } }


为什么setMessage()没有触发新一轮的变更检测?我认为当您更改UI中某些内容的值时,Angular 2会自动触发更改检测.
@drewmoore"任何改变绑定的东西都需要触发一轮变化检测".怎么样?这是一个好习惯吗?不应该一次完成所有事情吗?
只是要注意,在当前的问题体中,被调用的方法被命名为`updateMessage`,而不是`setMessage`
@Daynil,我有同样的感觉,直到我读到评论中给出的评论中的博客:https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4它解释了为什么需要手动完成.在这种情况下,角度变化检测具有生命周期.如果某些内容正在改变这些生命周期之间的值,则需要运行强制更改检测(或者设置超时 - 在下一个事件循环中执行,再次触发更改检测).
@Tycho,确实.自从我写了这篇评论以来,我回答了另一个问题,我在其中描述了[3种运行变化检测的方法](http://stackoverflow.com/a/35106069/215945),其中包括`detectChanges()`.

3> Biranchi..:

我通过从angular core添加ChangeDetectionStrategy来解决这个问题.

import {  Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'page1',
  templateUrl: 'page1.html',
})



4> Jo VdB..:

你不能使用,ngOnInit因为你只是改变成员变量message

如果要访问子组件的引用@ViewChild(ChildComponent),确实需要等待它ngAfterViewInit.

一个脏修复是updateMessage()使用例如setTimeout 调用下一个事件循环.

ngAfterViewInit() {
  setTimeout(() => {
    this.updateMessage();
  }, 1);
}


将我的代码更改为ngOnInit方法对我有用.

5> Jaswanth Kum..:

ngAfterViewChecked()为我工作:

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

constructor(private cdr: ChangeDetectorRef) { }
ngAfterViewChecked(){
   //your code to update the model
   this.cdr.detectChanges();
}



6> Maxim Korets..:

文章关于ExpressionChangedAfterItHasBeenCheckedError错误需要了解的所有内容都会详细解释行为.

您设置的问题ngAfterViewInit是在更改检测处理DOM更新后执行生命周期钩子.并且您正在有效地更改此挂钩中模板中使用的属性,这意味着需要重新呈现DOM:

  ngAfterViewInit() {
    this.message = 'all done loading :)'; // needs to be rendered the DOM
  }

这将需要另一个更改检测周期,而Angular by design只运行一个摘要周期.

你基本上有两个选择如何解决它:

异步更新属性或者使用setTimeout,Promise.then或在模板中引用异步观察到

在DOM更新之前在钩子中执行属性更新 - ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked.



7> MarmiK..:

为此,我已经尝试了以上答案,但许多版本在Angular的最新版本(6或更高版本)中均不起作用

我正在使用Material控件,该控件在完成第一次绑定后需要进行更改。

    export class AbcClass implements OnInit, AfterContentChecked{
        constructor(private ref: ChangeDetectorRef) {}
        ngOnInit(){
            // your tasks
        }
        ngAfterViewInit() {
            this.ref.detectChanges();
        }
    }

因此,请添加我的答案,这有助于解决某些特定问题。



8> RedPelle..:

您只需要在正确的生命周期钩子中更新您的消息,在这种情况下,ngAfterContentChecked而不是ngAfterViewInit,因为在ngAfterViewInit 中,已经启动了对变量消息的检查但尚未结束.

请参阅:https: //angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview

所以代码将是:

import { Component } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `
I'm {{message}}
`, }) export class App { message: string = 'loading :('; ngAfterContentChecked() { this.message = 'all done loading :)' } }

看看Plunker 的工作演示.

推荐阅读
可爱的天使keven_464
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有