我可以从字符串创建 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
?因此客户端决定在组件<my-component>simple or complex</my-component>
中使用简单内容还是复杂内容,因为这两个内容将在同一个<div>
(布局)中
【参考方案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 的提供者! (NgIf -> 模板参考)