如何正确处理依赖ViewChild和Angular 2中可观察到的数据的组件上的更改检测

How do I properly handle change detection on a component that relies on a ViewChild and data from an observable in Angular 2?

本文关键字:数据 组件 检测 观察 依赖 正确处理 ViewChild Angular      更新时间:2023-09-26

因此,目前,我有一个组件可以放入一个更大的面板中,用于呈现节点的直接父关系和子关系的图形。该组件应该在每次node_id输入从外部更改时刷新其图形。

我已经包含了代码的简化版本。

@Component({
  selector: 'relations',
  template: `
      <div [class]="'panel panel-' + (_loading ? 'default' : 'primary')">
          <div class="panel-heading">Child relations</div>
          <div class="panel-body">
              <div class="loading" *ngIf="_loading" style="text-align: center">
                  <img src="./loading.gif" height="100px" width="100px" />
              </div>
              <div class="graph_container" [style.display]="_loading ? 'none': 'block'" #my_graph></div>
          </div>
      </div>
    `
})
export class GraphComponent implements OnChanges {
    @Input('node_id') node_id;
    @ViewChild('my_graph') graphDiv;
    private _loading: boolean = true;
    private _current_node: Node;
    private _parent: Node;
    private _children: Node[];
    constructor(
        private _nodeService: NodeService
    ) {}
    ngOnChanges(changes){
        this.getRelations();
    }
    getRelations() {
        this._loading = true;
        Observable.combineLatest(
            this._nodeService.getEvent(this.node_id),
            this._nodeService.getChildren(this.node_id),
            this._nodeService.getParent(this.node_id)
        ).subscribe(v => {
            this._current_node = v[0];
            this._children = v[1];
            this._parent = v[2];
            this._loading = false
            this.renderGraph();
        });
    }
    renderGraph() {
        ...
    }
}

现在我遇到的问题是种族状况;CCD_ 2方法依赖于CCD_。因此,当可观察到的解析时,它可能会在@ViewChild组件初始化之前尝试调用renderGraph()

我试着通过做一些事情来使用生命周期挂钩,比如:

ngAfterViewInit(){
    if (!this._loading){
        this.renderGraph();
    }
}

只有当可观察到的在加载视图之前完成时,这才有帮助,并且如果视图首先完成渲染,则不会导致渲染任何图形。

所以我的问题是,我如何才能正确地实现我想要的?也就是说,响应于node_id的改变,在可观察的解析之后重新渲染图。

我是Angular 2的新手(通常是前端),我的直觉告诉我,我没有以预期的方式使用可观察到的,但我很难找到任何与我想要的相似的例子。

如有任何帮助/指导/建议,我们将不胜感激。

谢谢!

我会使用BehaviorSubject,它只是Observable的一种特殊类型。文档中的代码段:

它存储向消费者发出的最新值,每当新的Observer订阅时,它将立即从BehaviorSubject接收"当前值"。

首选BehaviorSubject的原因是,无论订阅实际发生在何时,它都会发出最后一个node_id值。如果它是在viewInit之前设置的。此外,因为它总是具有最新的值,所以我们不需要在GraphComponent上具有node_id属性。我们只需要一个setter,它会将传递的值发送给订阅者,并自动将其保存在主题上,这样每个新订阅者都会获得当前值。

import {BehaviorSubject} from 'rxjs/BehaviorSubject';
...
export class GraphComponent implements OnChanges {
    @ViewChild('my_graph') graphDiv;
    ...
    private _nodeIdSubject = new BehaviorSubject(-1);
    constructor(...) {}
    @Input('node_id') 
    set node_id(id){  // this is the same as ngOnChanges but will only be triggered if node_id changed
        this._nodeIdSubject.next(id);
    }
    ngAfterViewInit(){  // subscribe to node_id changes after view init
        this._nodeIdSubject.filter(n=> n > -1).subscribe(nodeId=> this.getRelations(nodeId));
    }
    getRelations(nodeId) {
        ...
    }
    renderGraph() {
        ...
    }
}

这可能不是最好的方法,但我喜欢它,因为现在您有了一个可以自由操作的node_id流。