【问题标题】:How can I implement a directive that wraps its HostElement's content in a div?如何实现将其 HostElement 的内容包装在 div 中的指令?
【发布时间】:2020-03-07 18:11:03
【问题描述】:

我需要实现一个指令,在其宿主元素中添加 HTML 元素,同时将所有元素的内容放在 HTML 的特定部分,本质上是包装内容,就像 Angular Material 如何包装 @ 的内容一样987654321@ 和<span class="mat-button-wrapper">

例如,组件将被放入这样的模板中:

<div my-directive>
    my text
</div>

这应该被渲染:

<div my-directive>
    <div class="my-class">
        my text
    </div>
</div>

我尝试过使用 WrapperComponent 并将其添加到指令的 ViewContainerRef 中,如下所示。

import { Directive, TemplateRef, ViewContainerRef, Component, Renderer2, ElementRef, ComponentFactoryResolver, ViewChild, ComponentRef } from '@angular/core';

@Component({
    selector: 'my-wrapper',
    template: `
        <div class="my-wrapper">
            <ng-content></ng-content>
        </div>
    `
})
export class WrapperComponent {
}

@Directive({
    selector: '[wrapContent]'
})
export class WrapContentDirective{

    wrapperRef: ComponentRef<WrapperComponent>;

    constructor(
        private renderer: Renderer2,
        private templateRef: TemplateRef<any>,
        private vcr: ViewContainerRef,
        private cfr: ComponentFactoryResolver,
        hostElement: ElementRef
    ) {
        const wrapperFactory = this.cfr.resolveComponentFactory(WrapperComponent);
        this.wrapperRef = this.vcr.createComponent(wrapperFactory);
    }
}

但是包装器是在元素旁边呈现的,而不是在它里面。而且我还需要弄清楚如何将 ng-content 的内容投影到 WrapperComponent 中。

我该如何实现?

【问题讨论】:

    标签: angular


    【解决方案1】:

    使用您想要的模板创建一个组件,我称之为 DynoComponent。该组件将被动态创建并在其上设置模板以与[ngTemplateOutlet] 一起使用。

    创建一个指令,该指令将捕获主机的TemplateRefViewContainerRef,并动态创建DynoComponent 并在其上设置模板属性。

    DynoComponent html

    <div class="my-class">
      <ng-template [ngTemplateOutlet]='template'></ng-template>
    </div>
    

    DynoComponent ts

    @Component({
      selector: 'app-dyno',
      templateUrl: './dyno.component.html',
      styleUrls: ['./dyno.component.css']
    })
    export class DynoComponent implements OnInit {
    
      template: TemplateRef<any>;
    
      constructor() { }
    
      ngOnInit(): void {
      }
    
    }
    

    我的指令

    @Directive({
      selector: '[appMyDirective]'
    })
    export class MyDirectiveDirective implements OnInit {
      compRef: ComponentRef<DynoComponent>;
      constructor(private vcRef: ViewContainerRef,
                  private templateRef: TemplateRef<any>,
                  private compFact: ComponentFactoryResolver) { }
    
      ngOnInit() {
        const myComp = this.compFact.resolveComponentFactory(DynoComponent);
        this.compRef = this.vcRef.createComponent(myComp);
        this.compRef.instance.template = this.templateRef;
      }
    
    }
    

    如何在其他模板中使用

    <div class="outer" *appMyDirective>
      <p>inside content</p>
    </div>
    

    输出

    <div _ngcontent-acn-c18="" class="my-class">
        <div _ngcontent-acn-c20="" class="outer">
            <p _ngcontent-acn-c20="">inside content</p>
        </div>
        <!--bindings={
          "ng-reflect-ng-template-outlet": "[object Object]"
        }-->
    </div>
    

    【讨论】:

    • 感谢您的回复。我考虑过使用结构指令,但我试图避免使用*指令。不过,我想这是正确的做法。我想知道 Angular Material 是如何做到的。还是谢谢!
    • 您可以添加一个与 TemplateRef 类型的指令同名的 setter 输入,并在 [appMyDirective]=‘templateRef’ 等主机元素上使用它,其中 templateRef 可以是对 ng-template 的本地引用。基本上这样你就不必使用 *.
    • 我刚刚测试过了。它有效,但我的目标是只包装元素的内容。该解决方案包装了所有内容,包括最初附加到指令的元素。
    猜你喜欢
    • 2017-06-06
    • 2015-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-13
    相关资源
    最近更新 更多