angularjs 2 rc4 Change detection: expression checked 后发生了变化

angularjs 2 rc4 Change detection: expression changed after it was checked

从 rc1 升级到 rc4 后,出现以下异常:

Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'

在网上研究了这个问题并阅读了所有可能的资料之后,我仍然不知道如何以 "angular" 的方式(没有超时等)来解决这个问题。

我知道 在开发模式下更改检测再次 运行, 只是为了确保没有任何更改。我也明白所有改变绑定的东西 'should trigger another round of change detection'.

我不明白的是为什么事件发生时没有自动触发新一轮检测如何更正。

我有一个非常简单的用例。最顶层的组件负责显示外层UI。它还包含一个应该是 hidden/shown 的控件。此行为由活动路由组件控制。

子实例化通过活动路由(路由器出口)发生,而不是手动进行。

所以这个最顶层的组件和所有子组件都订阅了共享服务中的事件发射器并发送事件。

最顶层的组件也在它的 Oninit 上订阅了这个事件。当它接收到事件时,它会更改局部变量。这反映在该组件的 html 上。

AppComponent

export class AppComponent implements OnInit {
  hidePageControls:boolean = true;

  constructor(private apiService: ApiService) {}

  ngOnInit(): any {
    let me = this;

    this.apiService.hidePageControls.subscribe(value => me.hidePageControls = value);
  }
}

子组件

ngOnInit() {
    me.apiService.hidePageControls.emit(false);
}

这背后的基本原理是,当用户导航到应用程序的不同部分时,会使用不同的组件。然后,这些组件将事件发送到最顶层的组件,以 show/hide 一个特定的控件。

我已经阅读了此问题的可能解决方案,包括 setTimeout()、直接调用更改检测等。人们还说我们不应该 'fight' 框架,我同意这一点。

因此,如果我认为子组件通知父组件的想法是 angular 中的 'wrong' 思维方式,请告诉我,一定要让我知道 'angular' 这样做的方式看起来像。


编辑

我找到的针对此类错误的唯一解决方案是使用超时:

分离变化检测器。 当事件被触发时,手动触发变化检测回合。

constructor(private apiService: ApiService, private categoryService: CategoryService, private router: Router, private cd: ChangeDetectorRef) {
    this.cd.detach();
}

.
.
.
.

this.apiService.hidePageControls.subscribe(value => {
    me.hidePageControls = value;
    setInterval(() => {
        me.cd.detectChanges();
    }, 100);
});

据我了解,这将导致 额外的开发变更检测 运行 由于超时而完成后进行一轮变更检测。我发现这是一个挑剔的解决方案。

如果有更好的,请告诉我。

我使用与 enable/disable 几乎相似的方式来控制取决于子元素的决定。 在 angular 更新 (rc1->rc4) 之前,它就像一个魅力。更新后,组件在第一次加载时开始中断。 重大变化是:

facade: change EventEmitter to be sync by default (#8761) (e5904f4)

link to issue

在子组件的初始化期间,父组件的值在其变化检测轮完成后发生变化。


修复你的问题可能是:

// in apiService
hidePageControls = new EventEmitter(true);

在 EventEmitter 构造函数中为 True 将强制其异步

希望对您有所帮助。

Angular 不喜欢变化检测导致模型发生变化。

ngOnInit() 由更改检测调用。

您可以使用 setTimeout() 延迟更改,直到更改检测完成。 setTimeout() 会导致应用程序范围内的更改检测 运行,因此不建议这样做。

您可以使用另一个未被变更检测调用的生命周期回调

据我所知 ngAfterViewInit 适用于这种情况。

ngAfterViewInit() {
    me.apiService.hidePageControls.emit(false);
}

另一种方法是注入 ChangeDetectorRef 并显式调用更改检测以使 Angular 在抛出之前知道更改:

constructor(cdRef: ChangeDetectorRef) {}

ngOnInit() {
    me.apiService.hidePageControls.emit(false);
    this.cdRef.detectChanges();
}