我可以从字符串创建 TemplateRef 吗?

Posted

技术标签:

【中文标题】我可以从字符串创建 TemplateRef 吗?【英文标题】:Can I create a TemplateRef from a string? 【发布时间】:2019-07-24 19:50:37 【问题描述】:

我得到的组件

假设我有以下超级简单的组件:

class MyComponent 
    @Input()
    simpleContent: string;
    @ContentChild(TemplateRef)
    content: TemplateRef<any>;

<div>
    <div *ngIf="simpleContent; else complexContent">simpleContent</div>
    <ng-template #complexContent>
        <ng-container [ngTemplateOutlet]="content"></ng-container>
    </ng-template>
</div>

根据用户的选择,可以这样使用:

<my-component simpleContent="Hello world!"></my-component>

也可以这样使用:

<my-component>
    <ng-template>Hello world!</ng-template>
</my-component>

虽然第二个选项允许嵌入 html,因此可以使用更复杂的格式,但在大多数情况下,用户可以选择仅包含一个属性的简单方法。

问题

我想简化组件并去掉模板中的 if/else。我想像这样:

class MyComponent 
    @ContentChild(TemplateRef)
    content: TemplateRef<any>;
    @Input()
    set simpleContent(value: string) 
        this.content = new TemplateRef(value);
    

<div>
    <ng-container [ngTemplateOutlet]="content"></ng-container>
</div>

显然这不起作用,因为new TemplateRef(value) 是不可能的。

有没有办法在运行时动态创建一个基本的简单模板?

【问题讨论】:

我认为这种方法:stackblitz.com/edit/… 更适合您的用例。 我不明白你为什么不使用ng-content?因此客户端决定在组件&lt;my-component&gt;simple or complex&lt;/my-component&gt; 中使用简单内容还是复杂内容,因为这两个内容将在同一个&lt;div&gt;(布局)中 【参考方案1】:

在这些示例中,我们实际上并没有“创建”templateRef,因为这需要角度渲染器工厂和大量额外代码来为新的 EmbeddedView 创建适当的注入器。我们只需要使用字符串值动态编辑模板就可以了。

示例 1:使用 ng-container

使用 ng-container 扩展 OP 问题的模板降价

<div>
    <ng-container #container></ng-container>
</div>
<!-- Adding an empty template for later use -->
<ng-template #content><ng-content></ng-content></ng-template>

那么组件的代码应该是这样的

export class Component implements AfterViewInit

    @ViewChild("content") contentRef: TemplateRef<any>;
    @ViewChild("container", read: ViewContainerRef) containerRef: ViewContainerRef;

    divRef: HTMLElement;
    value: string = "";
    @Input()
    set simpleContent(value: string) 
        if(this.divRef)
           this.divRef.innerHTML = this.value;
        
        this.value = value;
    

    constructor() 

    ngAfterViewInit(): void 
      let div = document.createElement("div");
      div.innerHTML = this.value;

      var embeddedViewRef = this.contentRef.createEmbeddedView(this.containerRef);
      this.containerRef.insert(embeddedViewRef);
      embeddedViewRef.rootNodes[0].replaceWith(div);
      this.divRef = div;
    

在这个例子中,我们获得了对位于 EmbeddedView 根节点中的 ng-content 标记的引用,并将其替换为我们的原生 html 模板字符串。

示例 2:使用 elementRef

由于我们在第一个示例中注入了 nativeElements,我想为什么不通过 ViewChild 和 ElementRef 的引用来直接编辑第一个 div 标签。

@Component(
    selector: "component",
    template:`<div #ref></div>`
)
export class Component implements AfterViewInit

    @ViewChild("ref", read: ElementRef) tref: ElementRef;
    
    value: string = "";
    @Input()
    set simpleContent(value: string) 
        if(this.tref) this.tref.nativeElement.innerHTML = this.value;
        this.value = value;
    
    constructor() 
    ngAfterViewInit(): void 
        this.tref.nativeElement.innerHTML = this.value;  
    

示例 3:使用简单的数据绑定

最后,既然它只是一个简单的字符串模板,我们想在组件中注入为什么不简单地在 innerHTML 上使用databinding

@Component(
    selector: "component",
    template:`<div [innerHTML]="value"></div>`
)
export class Component
    value: string = "";
    @Input()
    set simpleContent(value: string) 
        this.value = value;
    
    constructor() 
    


将近 2 年后,我怀疑 OP 仍在等待答案。这个回复对我来说主要是一个挑战,但我自己在研究中学到了一些东西。喜欢这篇关于dom manipulation techniques in angular的文章

希望这对那里的人有用

【讨论】:

以上是关于我可以从字符串创建 TemplateRef 吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何对以 TemplateRef 作为输入的 Angular 组件进行单元测试?

如何在 TemplateRef 中找到 Element

没有 TemplateRef 的提供者! (NgIf -> 模板参考)

typescript ElementRef,TemplateRef,ViewContainerRef

从单词中删除一个字母[重复]

我们可以同时使用内容类型八位字节流和 json 吗?