简体   繁体   English

在 Angular 结构指令中使用动态组件会产生额外的 HTML 标记。 如何删除或更换它?

[英]Using dynamic component within Angular structural directive produces extra HTML tag. How to remove or replace it?

I have quite complex infrastructure in my project which contains of我的项目中有相当复杂的基础设施,其中包含

  • host component主机组件
  • structural directive used in host component's template (MyDir)宿主组件模板 (MyDir) 中使用的结构指令
  • another component used in structural directive (MyComp)结构指令中使用的另一个组件(MyComp)

Simplified version looks like the following.简化版如下所示。

host component主机组件

@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' } ];
}

structural directive结构性指令

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;
  }
}

structural directive's component结构指令的组成部分

@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;
}

The output is output 是

custom td自定义 td
item 1第 1 项
item 2第 2 项

which is fine except the markup which is这很好,除了标记是

<table>
  <div my-comp>
    <tr><td>custom td</td></tr>
    <tr><td>item 1</td></tr>
    <tr><td>item 2</td></tr>
  </div>
</table>

Problem问题

I want to remove intermediate <div my-comp> from the result view or at least replace it with <tbody> .我想从结果视图中删除中间<div my-comp>或至少将其替换为<tbody> To see the whole picture I prepared Stackblitz DEMO , hope it will help... Also, it might be obvious the example is artificial, but this is what I came with trying to reproduce the issue with minimal code.要查看我准备的Stackblitz DEMO的全貌,希望它会有所帮助......另外,很明显这个例子是人为的,但这就是我试图用最少的代码重现问题的目的。 So the problem should have a solution in the given infrastructure.所以这个问题应该在给定的基础设施中有一个解决方案。

Update更新

@AlexG found simple way to replace intermediate div with tbody and stackblitz demo showed a good result at first. @AlexG 找到了用tbody替换中间div的简单方法,stackblitz 演示最初显示了一个很好的结果。 But when I tried to apply it to my project locally I faced new issue: browser arranges its own tbody before the dynamic contents of the table are ready to render, which results in two nested tbody in the end view, which seems inconsistent per html specs但是当我尝试在本地将它应用到我的项目时,我遇到了新问题:浏览器在表格的动态内容准备好呈现之前安排了自己的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 demo has no such problem, only tbody my-comp is present. Stackblitz 演示没有这样的问题,只有tbody my-comp存在。 But exactly the same project in my local dev environment does.但在我的本地开发环境中,完全相同的项目确实如此。 So I'm still trying to find a way how to remove intermediate my-comp container.所以我仍在尝试找到一种方法来删除中间my-comp容器。

Update 2更新 2

The demo had been updated in accordance with the solution suggested by @markdBC.演示已根据@markdBC 建议的解决方案进行了更新。

My answer is inspired by Slim's answer to a similar question found here: https://stackoverflow.com/a/56887630/12962012 .我的回答受到 Slim 对此处发现的类似问题的回答的启发: https://stackoverflow.com/a/56887630/12962012

You can remove the intermediate <div my-comp> by您可以通过以下方式删除中间<div my-comp>

  1. Creating a TemplateRef object representing the template of the MyComp component inside the MyComp component.创建一个TemplateRef object 代表MyComp组件内的MyComp组件的模板。
  2. Accessing this TemplateRef object from the structural directive.从结构指令访问此TemplateRef object。
  3. Creating an embedded view inside the view container with the TemplateRef object from the structural directive.使用结构指令中的TemplateRef object 在视图容器内创建嵌入式视图。

The resulting code looks something like:生成的代码如下所示:

MyComp component 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 directive 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);
  }
}

The resulting HTML looks something like:生成的 HTML 看起来像:

<table>
  <tr><td>custom td</td></tr>
  <tr><td>item 1</td></tr>
  <tr><td>item 2</td></tr>
</table>

I've prepared a StackBlitz demo at https://stackblitz.com/edit/table-no-div-wrapper .我在https://stackblitz.com/edit/table-no-div-wrapper准备了一个 StackBlitz 演示。

Change the selector of your component from [my-comp] to tbody [my-comp] and your will have a <tbody my-comp> instead of a <div my-comp> which would be sufficient if I understood you correctly.将组件的选择器从[my-comp]更改为tbody [my-comp] ,您将拥有一个<tbody my-comp>而不是<div my-comp>如果我理解正确就足够了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM