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

@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]

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


当前回答

简单:首先在你的组件结构中分离/移除变更检测,然后在ngAfterViewInit()方法中启用detectChanges()

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.
}

其他回答

在使用数据表时,我得到了类似的错误。当你在另一个*ngFor数据表中使用*ngFor时,会在它拦截角度变化周期时抛出这个错误。因此,不要在数据表内部使用数据表,而是使用一个常规的表或替换mf。带有数组名的数据。这很好。

正如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方法以防万一。

首先,请注意,这个异常只会在你以开发模式运行应用程序时被抛出(这是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 :)')
  }
}

我有几乎相同的案例,我有一系列的产品。我不得不让用户删除产品根据他们的选择。最后,如果数组中没有产品,那么我需要显示取消按钮而不是返回按钮,无需重新加载页面。

我通过检查ngAfterViewChecked()生命周期钩子中的空数组来完成。 这就是我是如何做到的,希望它有帮助:)

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

products: Product[];
someCondition: boolean;

constructor(private cdr: ChangeDetectorRef) {}

ngAfterViewChecked() {
  if(!this.someCondition) {
    this.emptyArray();
  }
}

emptyArray() {
    this.someCondition = this.products.length === 0 ? true : false;

    // run change detection explicitly
    this.cdr.detectChanges();
}

removeProduct(productId: number) {
    // your logic for removing product.
}

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

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!