簡體   English   中英

在 Angular 結構指令中使用動態組件會產生額外的 HTML 標記。 如何刪除或更換它?

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

我的項目中有相當復雜的基礎設施,其中包含

  • 主機組件
  • 宿主組件模板 (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;
}

output 是

自定義 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

演示已根據@markdBC 建議的解決方案進行了更新。

我的回答受到 Slim 對此處發現的類似問題的回答的啟發: https://stackoverflow.com/a/56887630/12962012

您可以通過以下方式刪除中間<div my-comp>

  1. 創建一個TemplateRef object 代表MyComp組件內的MyComp組件的模板。
  2. 從結構指令訪問此TemplateRef object。
  3. 使用結構指令中的TemplateRef object 在視圖容器內創建嵌入式視圖。

生成的代碼如下所示:

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 演示。

將組件的選擇器從[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