如何在 Angular 元素内插入动态组件

问题描述 投票:0回答:2

假设我们有一个如下的 html 结构

<div [innerHTML]="contentFromAPI | safeHTML"></div>

现在来自 api 的内容将是由更多 HTML 元素组成的字符串。使结构像这样:

<div>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
 <p></p>
</div>

我想动态创建一个组件并将其插入到子节点的中间。在这种情况下,排在第五位。

为此,我正在做这样的事情:

html 文件:

<div [innerHTML]="contentFromAPI | safeHTML" #articleContent></div>

ts 文件:

@ViewChild('articleContent', { read: ViewContainerRef })
  private articleContent!: ViewContainerRef;

ngOnChanges(){
 if(ads){
   this.addElementToTemplate();
 }
}

private addElementToTemplate() {
setTimeout(() => {
      const childrenLength =
        this.articleContent.element.nativeElement.children.length;
        this.articleContent.createComponent(DynamicElement,{index: Math.ceil(childrenLength/2)});
    }, 3000);
}

但这会将该组件添加为内容元素的同级元素。像这样的东西:

<div>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
     <p></p>
</div>
<dynamic-element></dynamic-element>
javascript html angular dom
2个回答
0
投票

可以在

innerHTML
内插入角度组件,但我们应该注意,以这种方式插入的元素位于角度上下文之外,并且更改检测将不起作用,因此它的行为就像普通的HTML元素一样。最好寻找替代方法而不是使用
innerHTML
,请查找下面的示例,强调我的观点,即更改检测在innerHTML 中不起作用

完整代码

import {
  ApplicationRef,
  Component,
  Renderer2,
  ViewChild,
  ViewContainerRef,
  createComponent,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { SafeHtmlPipe } from './safe-html.pipe';
import { CommonModule } from '@angular/common';
import { DynamicComponent } from './app/dynamic/dynamic.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [SafeHtmlPipe, CommonModule, DynamicComponent],
  template: `
    <div [innerHTML]="contentFromAPI | safeHtml" #articleContent></div>
  `,
})
export class App {
  @ViewChild('articleContent', { read: ViewContainerRef })
  private articleContent!: ViewContainerRef;
  name = 'Angular';
  contentFromAPI = `
  <p>1</p>
  <p>2</p>
  <p>3</p>
  <p>4</p>
  <p>5. insert here!</p>
  <p>6</p>
  <p>7</p>
  <p>8</p>
  <p>9</p>
  <p>10</p>
  `;

  constructor(
    private renderer2: Renderer2,
    private applicationRef: ApplicationRef
  ) {}

  ngAfterViewInit() {
    this.addElementToTemplate();
  }

  private addElementToTemplate() {
    const environmentInjector = this.applicationRef.injector;
    const componentRef = createComponent(DynamicComponent, {
      environmentInjector,
    });
    const insertElement = this.articleContent.element.nativeElement;
    console.log(insertElement);
    this.renderer2.appendChild(
      insertElement.children[4],
      componentRef.location.nativeElement
    );
    this.articleContent.createComponent(DynamicComponent, {
      index: 0,
    });
    componentRef.hostView.detectChanges();
  }
}

bootstrapApplication(App);

Stackblitz 演示


0
投票

根据我对问题的理解,这是一个可以在 .ts 文件中制定的解决方案。

this.contentFromAPI = //your data from API here;

const parser = new DOMParser();
const doc = parser.parseFromString(this.contentFromAPI, 'text/html');

// Get the element at the 5th position
//add logic to find middle node here instead of 4
const targetElement = doc.body.childNodes[4];

const componentElement = document.createElement('your-dynamic-component');


targetElement.parentNode.insertBefore(componentElement, targetElement);

// Update the contentFromAPI with the modified HTML structure
this.contentFromAPI = doc.body.innerHTML;

希望这会有所帮助:)

© www.soinside.com 2019 - 2024. All rights reserved.