繁体   English   中英

如何防止 Angular 8+ 组件在设置 Input 属性并且 ViewChild 准备好之前执行

[英]How can I prevent an Angular 8+ component from executing until an Input property is set and a ViewChild is ready

我编写了一个 Angular 组件,用于使用美人鱼 npm package渲染美人鱼图。

它按原样工作(见下文),但我想找到一种使用它的方法,而无需父组件包含*ngIf (以防止它在没有数据时尝试渲染)。 我希望组件本身能够决定是否调用mermaid.render()本身。 如果不使用*ngIf ,则当graphDefinitionnull时,组件的ngAfterContentInit()可能会运行。 这会导致mermaid库出错。

我可以使用ngOnChanges()或属性设置器来检测何时设置了值,但由于组件依赖于ViewChild ,我认为ngOnChanges()或属性设置器可能会在ViewChild准备好之前执行。

问题:您能否建议一种可靠地改进此组件的方法,以便不需要*ngIf

用法

图定义(字符串)作为Observable<string>从父组件中的 HTTP 调用提供的示例用法:

父组件.html

<mermaid-viewer
    *ngIf="graphDefinition$ | async as graphDefinition"
    [graphDefinition]="graphDefinition"
></mermaid-viewer>

零件

mermaid-viewer.component.html

<div #mermaid></div>

mermaid-viewer.component.ts

import {
    AfterContentInit,
    Component,
    Input,
    ViewChild
} from "@angular/core";
import * as mermaid from "mermaid";

@Component({
    selector: "mermaid-viewer",
    templateUrl: "./mermaid-viewer.component.html"
})
export class MermaidViewerComponent implements AfterContentInit {

    @ViewChild("mermaid", { static: true })
    public mermaidDiv;

    @Input()
    public graphDefinition: string;

    public ngAfterContentInit(): void {

        mermaid.initialize({
            theme: "default"
        });

        const element: any = this.mermaidDiv.nativeElement;
        mermaid.render(
            "graphDiv",
            this.graphDefinition,
            (svgCode, bindFunctions) => {
                element.innerHTML = svgCode;
            }
        );
    }
}

您的问题的完整答案过于复杂,它将涉及BehaviourSubjectcombineLatest等运算符。 但我试着给你一个提示。

您可以将Input与 setter 和 getter 结合使用,以便在输入更改时收到通知:

private _graphDefinition: string;
@Input()
public set graphDefinition(value) {
  this._graphDefinition = value;
  // .next() on BehaviourSubject
}
public get graphDefinition() {
  return this._graphDefinition;
}

你可以在 ViewChild 上做同样的事情,在这种情况下,当元素视图被初始化时,你只会发出一次:

private _mermaidDiv;
@ViewChild("mermaid", { static: true })
public set mermaidDiv(value) {
   this._mermaidDiv = value;
   // .next() on another BehaviourSubject
}
public get mermaidDiv() {
  return this._mermaidDiv;
}

然后你可以在combineLatest上使用ngAfterContentInit (我猜ngAfterViewInit也会很好):

ngAfterContentInit() {
  // ...
  combineLatest(
     bs1,
     bs2
  ).subscribe(result => {
     // do something with definition + view
     // result[0] => is your graph definition
     // result[1] => is your queried view
  });
  // ...
}

现在你可以在你的父组件中多次更改你的graphDefinition ,你的配置会自动更新

我想出了这个,灵感来自Cristian Traìna的答案,但没有使用BehaviourSubjectcombineLatest

render() function 将被调用两次。 第一次只有graphDefinitionmermaidDiv中的一个可用,它什么也不做。 第二次两个值都可用,然后调用mermaid.render 我认为它更简单一些,尽管可能没有那么优雅——如果需要多个属性,也无法很好地扩展。

import {
    Component,
    Input,
    ViewChild
} from "@angular/core";
import * as mermaid from "mermaid";

@Component({
    selector: "mermaid-viewer",
    templateUrl: "./mermaid-viewer.component.html"
})
export class MermaidViewerComponent {

    private hasRendered = false;

    private _mermaidDiv;
    @ViewChild("mermaid", { static: true })
    public set mermaidDiv(value) {
        this._mermaidDiv = value;
        this.render();
    }
    public get mermaidDiv() {
        return this._mermaidDiv;
    }

    private _graphDefinition: string;
    @Input()
    public set graphDefinition(value) {
        this._graphDefinition = value;
        this.render();
    }
    public get graphDefinition() {
        return this._graphDefinition;
    }

    private render() {
        if (this.graphDefinition && this.mermaidDiv) {

            this.hasRendered = true;

            mermaid.initialize({
                theme: "default"
            });

            const element = this.mermaidDiv.nativeElement as HTMLDivElement;
            mermaid.render(
                "graphDiv",
                this.graphDefinition,
                (svgCode, bindFunctions) => {
                    element.innerHTML = svgCode;
                }
            );
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM