【问题标题】:Angular structural directive and Renderer2Angular 结构指令和 Renderer2
【发布时间】:2023-03-02 21:21:01
【问题描述】:

我有一个自定义复选框指令,它添加了传统标签/跨度样式的样式以及一些其他功能。它在自身周围注入包装器并在旁边注入跨度。我刚刚意识到,当放置在结构指令中时,它无法操纵 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 的直接父级)我最终得到了初始元素,但没有注入: &lt;input type="checkbox" alloy alloyLabel="default"&gt;

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

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

【问题讨论】:

  • 几个问题 - 它是错误的,还是没有注入任何东西?即使指令不输出,输入元素是否存在?
  • 好问题,我的疏忽。我会补充这个问题。
  • 谢谢... 标签的“外部”是什么?我有一种感觉,这条线:this.labelElement = this.renderer.parentNode(el.nativeElement); 可能以某种方式涉及......也许 ng-container 以某种方式搞砸或干扰了父节点。
  • 是的,那是我跳出来的第一件事,但我怀疑它只是使 if 语句失败,这意味着生成了标签(实际上我认为 if 无论如何都过于谨慎)。但是,我的测试示例中的外部节点是:&lt;div style="max-width: 75rem; margin: auto;"&gt;
  • 我猜测构造函数在生命周期的某个较早时间点为容器的直接子代执行,而不是当 ngIf 变为真时......也许将 init 逻辑移动到 ngOnInit 会帮助?我不相信这是一个答案,但试一试......

标签: angular typescript angular2-directives angular-renderer2


【解决方案1】:

这可能是由于当一个元素位于 ng-container 中的顶层时,它的构造早于您的 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 中。

【讨论】:

    猜你喜欢
    • 2019-02-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-20
    • 1970-01-01
    • 2018-06-05
    • 2016-07-27
    • 1970-01-01
    • 2021-03-10
    相关资源
    最近更新 更多