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

yan*_*kee 17 angular

我得到的组件

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

class MyComponent {
    @Input()
    simpleContent: string;
    @ContentChild(TemplateRef)
    content: TemplateRef<any>;
}
Run Code Online (Sandbox Code Playgroud)
<div>
    <div *ngIf="simpleContent; else complexContent">{{simpleContent}}</div>
    <ng-template #complexContent>
        <ng-container [ngTemplateOutlet]="content"></ng-container>
    </ng-template>
</div>
Run Code Online (Sandbox Code Playgroud)

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

<my-component simpleContent="Hello world!"></my-component>
Run Code Online (Sandbox Code Playgroud)

或者它可以像这样使用:

<my-component>
    <ng-template>Hello world!</ng-template>
</my-component>
Run Code Online (Sandbox Code Playgroud)

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

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

class MyComponent {
    @ContentChild(TemplateRef)
    content: TemplateRef<any>;
    @Input()
    set simpleContent(value: string) {
        this.content = new TemplateRef(value);
    }
}
Run Code Online (Sandbox Code Playgroud)
<div>
    <ng-container [ngTemplateOutlet]="content"></ng-container>
</div>
Run Code Online (Sandbox Code Playgroud)

显然这行不通,因为 new TemplateRef(value)是不可能的。

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

Sim*_*gth 4

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

示例 1:使用 ng-container

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

<div>
    <ng-container #container></ng-container>
</div>
<!-- Adding an empty template for later use -->
<ng-template #content><ng-content></ng-content></ng-template>
Run Code Online (Sandbox Code Playgroud)

那么组件的代码将如下所示

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,我们获取对位于 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;  
    }
}
Run Code Online (Sandbox Code Playgroud)

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

最后,由于它只是一个简单的字符串模板,我们想要注入到组件中,为什么不简单地在innerHTML上使用数据绑定

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

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

希望这对那里的人有用