如何设置角度组件的动态“ id” HTML属性?

时间:2018-08-20 19:21:32

标签: javascript angular dependency-injection

我的问题与在同一页面上放置具有不同参数的相同组件有关。在这种情况下,它是一个组件,其中包含来自第三方Javascript库(D3JS)的图表,该图表需要HTML id属性来查找和修改组件的HTML内容。

现在,此id属性应该为页面上放置的每个图表都包含一个唯一的字符串,并且如果我直接将其设置为来自父组件的字符串,则可以正常工作:

<my-chart id="gaugeChart0"></my-chart>

我猜想它起作用的原因是因为id属性在组件创建时就已经存在,并且任何尝试访问它的代码都可以立即实现。

然而,此图表又嵌入了bootstrap 4卡布局中,如下所示:

<div class="row">
    <div class="col-6">
        <div class="card">
            <div class="card-body">
                <div class="row">
                    <div class="col-5">
                        <my-chart id="gaugeChart0"></my-chart>
                    </div>
                    <div class="col-7">
                        ... Some other widgets ...
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-6">
        <div class="card">
            <div class="card-body">
                <div class="row">
                    <div class="col-5">
                        <my-chart id="gaugeChart1"></my-chart>
                    </div>
                    <div class="col-7">
                        ... Some other widgets ...
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

现在,为了使其更加方便(并轻松将单击事件绑定到整个块等),我想将以<div class="card">开头的整个部分提取到一个新组件中。

比方说,我将这个新组件称为 WidgetContainerComponent ,其中包含图表以及引导卡布局,包括在那里定义的其他窗口小部件。

使用此包装器组件时得到的代码为:

<div class="row">
    <div class="col-6">
        <widget-container chartId="gaugeChart0"></widget-container>
    </div>
    <div class="col-6">
        <widget-container chartId="gaugeChart1"></widget-container>
    </div>
</div>

为了使其正常工作, WidgetContainerComponent 具有一个输入字段

@Input() chartId: string;

可以设置的

然后我要做的是将 MyChartComponent id属性设置为已设置为chartId的字符串:

<div class="card">
    <div class="card-body">
        <div class="row">
            <div class="col-5">
                <my-chart [id]="chartId"></my-chart>
            </div>
            <div class="col-7">
                ... Some other widgets ...
            </div>
        </div>
    </div>
</div>

但是这是行不通的,因为angular为id属性添加了前缀,从而导致类似ng-reflect-id的情况。

我还尝试按照herehere所述,使用[attr.id]设置属性:

<div class="card">
    <div class="card-body">
        <div class="row">
            <div class="col-5">
                <my-chart [attr.id]="chartId"></my-chart>
            </div>
            <div class="col-7">
                ... Some other widgets ...
            </div>
        </div>
    </div>
</div>

这会导致 MyChartComponent 具有直接的id属性,但似乎仅在组件生命周期的稍后阶段添加。

我还尝试仅在ngOnInitngAfterContentInit中的 MyChartComponent 中初始化图表,但这并不起作用。

任何建议或想法都非常欢迎!

1 个答案:

答案 0 :(得分:1)

因此,我找到了解决此问题的方法。问题是双重的:

  1. 第一个问题与具有真实id属性的要求有关,而属性名称以ng-reflect-前缀有角。

    我不知道的是,从Angular 4开始,此前缀被添加到在开发模式下被声明为该组件的@Input变量的任何属性中(有关详细说明,请参见{{3 }})。

    解决此问题的方法是写[attr.id]=chartId而不是[id]=chartId。原因是在这种情况下,我需要设置一个 HTML属性,但是使用方括号符号创建了一个属性绑定。为了通过属性绑定语法动态设置HTML属性,您必须在属性名称上添加前缀attr.

    有关所有有效绑定语法的完整概述,请参见this post。 专门回顾如何进行属性绑定,请参见Angular Docs

  2. 第二个问题是,当尚未创建组件的id属性时,我尝试访问它。此问题与Angular的生命周期挂钩有关,该阶段有不同的阶段。有关Angular生命周期挂钩的完整概述,请参见this section

    由于我本人并不十分了解它,因此我将在此处详细说明。

    Angular带有一组固定的生命周期钩子,每次安装组件时都会调用它们,它们也以预定义的顺序被调用。挂钩的定义如下:

    1. 构造函数
    2. ngOnChanges
    3. ngOnInit
    4. ngDoCheck
      1. ngAfterContentInit
      2. ngAfterContentChecked
      3. ngAfterViewInit
      4. ngAfterViewChecked
    5. ngOnDestroy

    _

    重要的是,在一个步骤中创建或准备的元素只能在任何后续步骤中访问。在我的情况下,我将用于图表初始化的所有代码都放入了ngAfterContentInit钩子中,但是只有在调用ngAfterViewInit时该组件才完全加载。

    最后,解决方案是将预初始化代码放入构造函数中,并将图表初始化代码放入ngAfterViewInit方法中。在此阶段,id属性已正确创建,可以由图表访问。