如何正确更改导致父组件中视图更改的`Observable`?

时间:2016-07-08 08:59:32

标签: angular

我有一个组件可以更改应用程序全局服务中的值,有时会看到Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'个消息。我知道这些可以通过启用生产模式来沉默,但我更愿意理解如何正确地做到这一点。

tl; dr:怀疑这可以通过某种方式告诉变更检测器父母也会改变来解决,所以我该如何改变 - 检测树?在父母和孩子之间存在<router-outlet>可能重要也可能不重要。

完整的解释,如果我有完全错误的理解: 有问题的代码大致如下所示:ChildComponent有两个需要完成的订阅,一旦准备就会显示组件,一些视图变量将重置为有意义的值。

this._routeParams.params.subscribe(params => {
    var pageId = params['pageId'];
    this._projectService.activeProject
        .subscribe(res => {
            // Project is loaded, display the correct page to edit
            this._project = res;
            this._page = this._project.getPageById(pageId);

            // The active page has changed: Reset render preview and sidebar
            this.doRenderPreview = false;
            // <<ERROR>> triggered by this line
            this._sidebarService.showSidebar(SidebarComponent.SIDEBAR_IDENTIFIER);
        });
})

SidebarService.showSidebar()的调用基本上会更新此全局可用Observable中的SidebarService,然后用于实际呈现ParentComponent中的侧边栏。

// this.model is a BehaviourSubject
showSidebar(newType : string, param? : any) {
    if (!this.isKnownType(newType)) {
        throw new Error(`Unknown sidebar type: ${newType}`);
    }

    this._model.next({
        type : newType,
        param : param
    });
}

还有另一个可观察对象只传播有关可见性状态的变化,这对于需要知道是否存在侧边栏但不关心细节的组件非常重要。

get isSidebarVisible() : Observable<boolean> {
    return (this._model.map(s => !!s));
}

为了呈现侧边栏,ParentComponent需要更新其布局,这就是错误发生的地方:

<!-- Error: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true' -->
<div *ngIf="isSidebarVisible | async"
     class="sidebar">
  <sidebar-loader></sidebar-loader>
</div>
<div *ngIf="isSidebarVisible | async"
     class="sidebar-hack">
  <!-- Dirty hack to ensure the main content is not too wide -->
</div>

isSidebarVisible属性只是全局服务的快捷方式:

get isSidebarVisible() {
    return (this._sidebarService.isSidebarVisible);
}

怀疑问题是Editor组件的更改会导致更改其父组件。但我在其他地方确实有类似的代码,这个场景的一个关键区别可能是通过<router-outlet>加载孩子吗?

编辑:在视图中避开async管道时的行为是相同的,而不是&#34;缓存&#34;父级中的isSidebarVisible并通过订阅更新它:this._sidebarService.isSidebarVisible.subscribe(v => this._sidebarVisible = v);

2 个答案:

答案 0 :(得分:1)

要在树中传播更改,您可能需要使用EventEmitter,这是一个指南:https://toddmotto.com/component-events-event-emitter-output-angular-2

但是,我认为您应该在全局变量中设置对observable的订阅,将isSidebarVisible设置为true或false,并将isSidebarVisible: boolean = false;设置为简单的布尔值。然后,一旦侧边栏可见,您只需将此值设置为true。

答案 1 :(得分:1)

适合您的解决方案是ParentComponent应该通知变更检测发生了变化。所以你可以

  • 将ChangeDetector注入您的组件: constructor(private cd: ChangeDetectorRef) {}

  • 订阅_sidebarService.isSidebarVisible的更改并更新布局并标记组件以进行更改检测:

ngOnInit() {
    this._sidebarService.isSidebarVisible.subscribe((visibility) => {
      this.visibility = visibility; // application state changed
      this.cd.markForCheck(); // marks path
    })
}

以下是一些详细信息:http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html