在 Angular 结构指令中使用动态组件会产生额外的 HTML 标记。如何删除或更换它?
Posted
技术标签:
【中文标题】在 Angular 结构指令中使用动态组件会产生额外的 HTML 标记。如何删除或更换它?【英文标题】:Using dynamic component within Angular structural directive produces extra HTML tag. How to remove or replace it? 【发布时间】:2020-03-08 15:32:12 【问题描述】:我的项目中有相当复杂的基础设施,其中包含
主机组件 宿主组件模板 (MyDir) 中使用的结构指令 结构指令中使用的另一个组件 (MyComp)简化版如下所示。
主机组件
@Component(
selector: 'my-app',
template: `
<table>
<tr *myDir="let item of data">
<td>item.text</td>
</tr>
</table>`
)
export class AppComponent
data = [ text: 'item 1' , text: 'item 2' ];
结构指令
import MyComp from './myComp';
@Directive( selector: '[myDir][myDirOf]' )
export class MyDir implements OnInit
private data: any;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private resolver: ComponentFactoryResolver
)
@Input() set myDirOf(data: any)
this.data = data;
ngOnInit()
const templateView = this.templateRef.createEmbeddedView();
const compFactory = this.resolver.resolveComponentFactory(MyComp);
const componentRef = this.viewContainer.createComponent(
compFactory, undefined, this.viewContainer.injector, [templateView.rootNodes]
);
componentRef.instance.data = this.data;
componentRef.instance.template = this.templateRef;
结构指令的组件
@Component(
selector: '[my-comp]',
template: `
<tr><td>custom td</td></tr>
<ng-template *ngFor="let item of data"
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]=" $implicit: item "
></ng-template>`
)
export class MyComp
public template: TemplateRef<any>;
public data: any;
输出是
自定义 td 项目 1 第 2 项
这很好,除了标记是
<table>
<div my-comp>
<tr><td>custom td</td></tr>
<tr><td>item 1</td></tr>
<tr><td>item 2</td></tr>
</div>
</table>
问题
我想从结果视图中删除中间的<div my-comp>
,或者至少用<tbody>
替换它。要查看我准备的Stackblitz DEMO 的全貌,希望它会有所帮助......另外,很明显这个例子是人为的,但这就是我试图用最少的代码重现问题的目的。所以这个问题应该在给定的基础设施中有一个解决方案。
更新
@AlexG 找到了用tbody
替换中间div
的简单方法,stackblitz 演示一开始就显示出很好的结果。但是当我尝试将它应用到本地项目时,我遇到了新问题:浏览器在表格的动态内容准备好呈现之前安排了自己的tbody
,这导致最终视图中有两个嵌套的tbody
,这似乎每个 html 规范不一致
<table>
<tbody>
<tbody my-comp>
<tr><td>custom td</td></tr>
<tr><td>item 1</td></tr>
<tr><td>item 2</td></tr>
</tbody>
</tbody>
</table>
Stackblitz 演示没有这样的问题,只有tbody my-comp
存在。但在我的本地开发环境中,完全相同的项目确实如此。所以我仍在尝试找到一种方法来删除中间my-comp
容器。
更新 2
demo 已根据@markdBC 建议的解决方案进行了更新。
【问题讨论】:
【参考方案1】:我的回答灵感来自 Slim 对类似问题的回答:https://***.com/a/56887630/12962012。
您可以通过
删除中间的<div my-comp>
-
在
MyComp
组件内创建一个代表MyComp
组件模板的TemplateRef
对象。
从结构指令访问此TemplateRef
对象。
使用结构指令中的TemplateRef
对象在视图容器内创建嵌入视图。
生成的代码如下所示:
MyComp
组件
@Component(
selector: "[my-comp]",
template: `
<ng-template #mytemplate>
<tr>
<td>custom td</td>
</tr>
<ng-template
*ngFor="let item of data"
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]=" $implicit: item "
></ng-template>
</ng-template>
`
)
export class MyComp
public template: TemplateRef<any>;
public data: any;
@ViewChild("mytemplate", static: true ) mytemplate: TemplateRef<any>;
MyDir
指令
export class MyDir implements OnInit
private version: string;
private data: any;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private resolver: ComponentFactoryResolver
)
@Input() set myDirOf(data: any)
this.data = data;
ngOnInit()
const compFactory = this.resolver.resolveComponentFactory(MyComp);
const componentRef = compFactory.create(this.viewContainer.injector);
componentRef.instance.data = this.data;
componentRef.instance.template = this.templateRef;
this.viewContainer.createEmbeddedView(componentRef.instance.mytemplate);
生成的 HTML 类似于:
<table>
<tr><td>custom td</td></tr>
<tr><td>item 1</td></tr>
<tr><td>item 2</td></tr>
</table>
我在https://stackblitz.com/edit/table-no-div-wrapper 准备了一个 StackBlitz 演示。
【讨论】:
看起来这是您对 SO 的第一个答案,它非常有帮助!请继续贡献,社区需要你!【参考方案2】:将组件的选择器从[my-comp]
更改为tbody [my-comp]
,您将拥有<tbody my-comp>
而不是<div my-comp>
,如果我理解正确的话就足够了。
【讨论】:
我更新了问题...似乎tbody
有问题,浏览器太聪明了,您的方法导致嵌套tbody
在真实环境中出现以上是关于在 Angular 结构指令中使用动态组件会产生额外的 HTML 标记。如何删除或更换它?的主要内容,如果未能解决你的问题,请参考以下文章