角结构指令和Renderer2

时间:2018-09-11 12:49:49

标签: angular typescript angular2-directives angular-renderer2

我有一个自定义的checkbox指令,该指令添加了传统标签/跨度样式的样式以及其他一些功能。它在自身周围以及跨度上注入包装器。我只是意识到,当放置在结构指令中时,它将无法操纵DOM。大部分设置都是在构造函数中完成的,但我认为也许这需要对Angular生命周期有所了解,才能与结构化父级一起很好地发挥作用。

示例问题DOM:

  <ng-container *ngIf="test">
    <!-- <div class="row align-middle"> -->
      <input type="text" alloy placeholder="you should see a checkbox">
      <input type="checkbox" alloy alloyLabel="default">
    <!-- </div> -->
  </ng-container>

使用已注释的div可以正常工作。但是,使用ng-container作为直接父级时,渲染器无法注入DOM。这是构造函数:

constructor(
    protected el: ElementRef,
    protected renderer: Renderer2,
    protected focusMonitor: FocusMonitor,
    @Host() @Optional() protected identityDirective: AlloyIdentityDirective) {
    super();

    // If we don't have a label wrapper, create one
    this.labelElement = this.renderer.parentNode(el.nativeElement);
    if (!(this.labelElement instanceof HTMLLabelElement)) {
        const label = this.renderer.createElement('label');

        // Inject wrapper then move native element (input) within it.
        this.renderer.insertBefore(this.labelElement, label, this.el.nativeElement);
        this.renderer.removeChild(this.labelElement, this.el.nativeElement);
        this.renderer.appendChild(label, this.el.nativeElement);
        this.labelElement = label;

    // We must add the span because that's what actually gets the check styling
    this.styledElement = this.renderer.createElement('span');
    this.renderer.appendChild(this.labelElement, this.styledElement);
}

编辑:添加了结果DOM

没有实际错误。在有缺陷的情况下(ng-container的直接父级),我得到了初始元素,但没有注入: <input type="checkbox" alloy alloyLabel="default">

使用包装器div,我得到了预期的注入(已删除_ngcontent *):

<label class="alloy-check-wrapper">
  <input alloy="" alloylabel="default" type="checkbox" class="alloy-check">
  <span></span>
  <span class="alloyLabel">default</span>
</label>

1 个答案:

答案 0 :(得分:1)

这可能是由于以下事实导致的:当元素位于ng容器内的顶层时,它的构造要比ngIf变为true时要早,即在将其添加到DOM之前。

要解决此问题,您需要将修改DOM的逻辑从构造函数移至ngOnInit,例如:

constructor(
    protected el: ElementRef,
    protected renderer: Renderer2,
    protected focusMonitor: FocusMonitor,
    @Host() @Optional() protected identityDirective: AlloyIdentityDirective) {
    super();
}

ngOnInit() {
    // If we don't have a label wrapper, create one
    this.labelElement = this.renderer.parentNode(el.nativeElement);
    if (!(this.labelElement instanceof HTMLLabelElement)) {
        const label = this.renderer.createElement('label');

        // Inject wrapper then move native element (input) within it.
        this.renderer.insertBefore(this.labelElement, label, this.el.nativeElement);
        this.renderer.removeChild(this.labelElement, this.el.nativeElement);
        this.renderer.appendChild(label, this.el.nativeElement);
        this.labelElement = label;
    }

    // We must add the span because that's what actually gets the check styling
    this.styledElement = this.renderer.createElement('span');
    this.renderer.appendChild(this.labelElement, this.styledElement);
}

对于修改DOM而言,无论如何这几乎肯定是一个更好的做法,因为在构造函数执行的那一刻,您无法确定地说它是否正确存在于DOM中。