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

@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 :)'
  }
}

扔:

例外:表达式'I'm {{message}} in App@0:5'在被检查后发生了变化。之前的值:'I'm loading:('。当前值:'I'm all done loading:)' in [I'm {{message}} in App@0:5]

当我所做的一切都是更新一个简单的绑定时,我的视图被启动?


当前回答

为此,我尝试了上面的答案,其中许多在最新版本的Angular(6或更高版本)中都不起作用。

我正在使用材料控制,需要在第一次绑定完成后进行更改。

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

加上我的答案,这有助于解决一些具体问题。

其他回答

ngAfterViewChecked()为我工作:

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

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

首先,请注意,这个异常只会在你以开发模式运行应用程序时被抛出(这是beta-0的默认情况):如果你在引导应用程序时调用enableProdMode(),它不会被抛出(参见updated plunk)。

其次,不要这样做,因为抛出这个异常是有充分理由的:简而言之,当处于开发模式时,每一轮更改检测之后都会立即进行第二轮,以验证自第一轮结束以来没有绑定发生更改,因为这将表明更改是由更改检测本身引起的。

在你的plunk中,绑定{{message}}是通过调用setMessage()来改变的,这发生在ngAfterViewInit钩子中,它作为初始变更检测回合的一部分发生。不过这本身并没有问题——问题是setMessage()改变了绑定,但不会触发新一轮的变更检测,这意味着直到在其他地方触发了未来的变更检测,才会检测到此更改。

结论:任何更改绑定的操作都需要触发一轮更改检测。

更新以回应所有关于如何做到这一点的例子的请求:@Tycho的解决方案是有效的,就像回答@MarkRajcok指出的三种方法一样。但坦率地说,我觉得他们都很丑陋,很错误,就像我们在ng1中习惯依赖的那种黑客一样。

可以肯定的是,在偶尔的情况下,这些hack是合适的,但如果您只是在非常偶然的基础上使用它们,这表明您正在与框架作斗争,而不是完全接受它的响应性。

恕我直言,更习惯的“Angular2方式”是这样的:(砰)

@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 :)')
  }
}

正如drewmoore所说,在这种情况下,正确的解决方案是手动触发当前组件的变更检测。这是使用ChangeDetectorRef对象(从angular2/core导入)的detectChanges()方法完成的,或者它的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();
  }

}

这里还有Plunkers演示了ngOnInit, setTimeout和enableProdMode方法以防万一。

我不能评论@Biranchi的帖子,因为我没有足够的声誉,但它为我解决了这个问题。

有一件事要注意! 如果添加changeDetection: ChangeDetectionStrategy。OnPush在组件上没有工作,它是一个子组件(哑组件)尝试添加到父组件也。

这修复了bug,但我想知道这有什么副作用。

我认为最简单的解决方法如下:

Make one implementation of assigning a value to some variable i.e. via function or setter. Create a class variable (static working: boolean) in the class where this function exists and every time you call the function, simply make it true whichever you like. Within the function, if the value of working is true, then simply return right away without doing anything. else, perform the task you want. Make sure to change this variable to false once the task is completed i.e. at the end of the line of codes or within the subscribe method when you are done assigning values!