AngularJS 2 RC4变更检测:表达式在检查后发生了改变

4

在从rc1升级到rc4后,我遇到了以下异常:

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

在网上研究并阅读了所有相关内容之后,我仍然不知道如何以“angular”方式(无需使用timeout等)解决这个问题。

我明白当开发模式更改检测时会再次运行,只是为了确保没有改变的内容。我也知道所有更改绑定的操作都应该触发另一轮更改检测

我不明白的是为什么事件发生时没有自动触发新的检测轮回以及如何来纠正它。

我有一个非常简单的用例。最上层的组件负责显示外部用户界面,它还包含一个控件,应该被隐藏/显示。此行为由活动路由组件控制。

子级实例化由活动路由(router-outlet)而不是手动执行。

因此,此最上层组件和所有子组件都订阅了共享服务中的一个事件发射器并发送事件。

最上层组件还在其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);
}

这样做的原理是当用户导航到应用程序的不同部分时,使用不同的组件。这些组件然后向顶层组件发送事件以显示/隐藏特定控件。
我已经阅读了可能的解决方案,包括setTimeout(),直接调用变更检测等。人们也说我们不应该“与框架对抗”,我同意这种看法。
因此,请告诉我在Angular中,子组件通知父组件的想法是否是“错误”的思考方式,并让我知道如何以“Angular”的方式执行此操作。
编辑
我找到的唯一解决此类错误的方法是使用setTimeout(),如下所示:
分离变更检测器。 当事件被触发时,手动触发变更检测轮。
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);
});

据我理解,由于超时而进行的额外开发变更检测运行结束后,这将导致一轮变更检测。我认为这是一个琐碎的解决方案。
如果有更好的解决方案,请告诉我。

这个问题有什么新进展吗?你找到其他处理方法或构建服务/组件的方式了吗?谢谢! - Bruno João
我还没有找到任何“angular”的做这种事情的方式。我选择了手动更改检测和超时,如上所述。我将等待第一个版本再次研究这个问题。 - sm0ke21
2个回答

1

我曾经使用了几乎相似的方法来根据子元素的决定启用/禁用控件。在 Angular 升级 (rc1->rc4) 之前,它像魅力一样运作良好。升级后,组件在首次加载时开始出现问题。

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

到问题的链接

父组件的值在其更改检测轮结束期间发生变化,在子组件的初始化期间。


解决您的问题的方法可能是:

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

在 EventEmitter 构造函数中使用 True 将强制其异步执行。

希望这能帮到你。


hidePageControls = new EventEmitter<boolean>(true); 如果需要进行类型化,请使用此代码 - Goris
这种通过向构造函数提供值的方法似乎在对rc4有用。 - sm0ke21

0

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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接