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

dre*_*ore 241 typescript angular

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

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message:string = 'loading :(';

  ngAfterViewInit() {
    this.updateMessage();
  }

  updateMessage(){
    this.message = 'all done loading :)'
  }
}
Run Code Online (Sandbox Code Playgroud)

投掷:

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

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

Tyc*_*cho 246

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

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

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App implements AfterViewInit {
  message: string = 'loading :(';

  constructor(private cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.message = 'all done loading :)'
    this.cdr.detectChanges();
  }

}
Run Code Online (Sandbox Code Playgroud)

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

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

dre*_*ore 151

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

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

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

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

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

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

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

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message | async}} </div>`
})
export class App {
  message:Subject<string> = new BehaviorSubject('loading :(');

  ngAfterViewInit() {
    this.message.next('all done loading :)')
  }
}
Run Code Online (Sandbox Code Playgroud)

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

Bir*_*chi 42

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

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


Jo *_*VdB 34

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

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

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

ngAfterViewInit() {
  setTimeout(() => {
    this.updateMessage();
  }, 1);
}
Run Code Online (Sandbox Code Playgroud)

  • 将我的代码更改为ngOnInit方法对我有用. (3认同)

Jas*_*mar 34

ngAfterViewChecked()为我工作:

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

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

  • 唯一的问题是每次您在屏幕上执行任何操作时它都会运行 (2认同)

Sha*_*asi 29

我从 AfterViewInit 切换到 AfterContentChecked 并且它对我有用。

这是过程

  1. 在构造函数中添加依赖项:

    constructor (private cdr: ChangeDetectorRef) {}

  2. 并在此处使用已实现的方法代码调用您的登录名:

     ngAfterContentChecked() {
         this.cdr.detectChanges();
      // call or add here your code
     }
    
    Run Code Online (Sandbox Code Playgroud)

  • 对我来说,使用“ngAfterContentChecked”在性能方面似乎并不是明智之举。在我的小示例中,即使在 Angular 完成视图初始化后滚动,代码也会执行多次。对性能有什么想法吗? (2认同)

Max*_*kyi 22

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

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

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

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

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

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

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


sha*_*ain 14

出现此错误是因为现有值在初始化后立即更新。因此,如果您在 DOM 中呈现现有值后更新新值,那么它会正常工作。就像这篇文章中提到的Angular Debugging “Expression has changed after it was checked”

例如你可以使用

ngOnInit() {
    setTimeout(() => {
      //code for your new value.
    });
Run Code Online (Sandbox Code Playgroud)

}

或者

ngAfterViewInit() {
  this.paginator.page
      .pipe(
          startWith(null),
          delay(0),
          tap(() => this.dataSource.loadLessons(...))
      ).subscribe();
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我没有在 setTimeout 方法中提到时间。因为它是浏览器提供的 API,而不是 JavaScript API,所以这将在浏览器堆栈中单独运行,并等待调用堆栈项完成。

Philip Roberts 在 Youtube 视频之一中解释了浏览器 API 如何激发概念(黑客是什么事件循环?)。


Mar*_*miK 9

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

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

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

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

  • 如果以上都不起作用,我推荐这个答案。 (2认同)
  • 每次重新计算或重新检查 DOM 时都会调用 afterContentChecked。来自 Angular 版本 9 的暗示 (2认同)

Red*_*lle 7

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

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

所以代码将是:

import { Component } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message: string = 'loading :(';

  ngAfterContentChecked() {
     this.message = 'all done loading :)'
  }      
}
Run Code Online (Sandbox Code Playgroud)

看看Plunker 的工作演示.

  • 上面的 `ngAfterContentChecked` 和 `ChangeDetectorRef` 的组合对我有用。在 `ngAfterContentChecked` 上调用 - `this.cdr.detectChanges();` (2认同)

Was*_*siF 6

简单:首先在组件构造中分离/删除更改检测,然后detectChanges()ngAfterViewInit()方法中启用

constructor(private cdr: ChangeDetectorRef) {
  this.cdr.detach() // detach/remove the change detection here in constructor
}


ngAfterViewInit(): void {
  // do load objects or other logics here
  
  // at the end of this method, call detectChanges() method.
  this.cdr.detectChanges(); // enable detectChanges here and you're done.
}
Run Code Online (Sandbox Code Playgroud)


Tob*_*ann 5

您还可以在 ngOnInt()-Method 中调用 updateMessage() ,至少它对我有用

ngOnInit() {
    this.updateMessage();
}
Run Code Online (Sandbox Code Playgroud)

在 RC1 中,这不会触发异常


归档时间:

查看次数:

155352 次

最近记录:

6 年 前