Angular 结构指令和 Renderer2

标签 angular typescript angular2-directives angular-renderer2

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

带 wrapper div我得到了预期的注入(inject)(删除了 _ngcontent*):

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

最佳答案

这很可能是因为当一个元素位于 ng-container 的顶层时,它的构造早于 ngIf 变为真时,即在它被添加到 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 中。

关于Angular 结构指令和 Renderer2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52276376/

相关文章:

Angular 6 Form - 在 ValueChanges 之后改变值

angular - Angular 在文本输入中只允许数字

javascript - d3.js v5.9.1 - 此版本中的某些方法未按预期工作

Angular:通过环境设置 APP_BASE_HREF 无效

angular - 管道转换中的 promise

angularjs - 注入(inject) ui-router resolve 指令

javascript - Angular - 高度和宽度的属性绑定(bind)不适用于图像

javascript - 在创建 Angular 1 应用程序时,我应该牢记哪些 Angular 2 方面?

angularjs - 如何将 `link` 函数替换为 Angular 2 组件中的目标 DOM 元素

node.js - 带有 TypeScript : Declaration expected compiler error after @component 的 Angular2