以角度 2/4 动态添加组件

Posted

技术标签:

【中文标题】以角度 2/4 动态添加组件【英文标题】:Dynamically add Component in angular 2/4 【发布时间】:2018-08-11 07:25:52 【问题描述】:

如何动态添加组件?

toolbar.component.ts:

@Component(
  selector: 'app-toolbar',
  template: '<button>Add Text component</button>'
)
export class ToolbarComponent 
   constructor()  
  

section.component.ts:

@Component(
   selector: 'div[app-type=section]',
   template: ''
)
export class SectionComponent 
   constructor()  
  

text.component.ts:

@Component(
   selector: 'app-text',
   template: '<p>This is dynamically component</p>'
)
export class TextComponent 
   constructor()  
  

view.component.ts:

@Component(
   selector: 'app-view',
   template: `<div class="container">
<app-toolbar></app-toolbar>
<div app-type="section" id="SECTION1" class="active"></div>
<div app-type="section" id="SECTION2"></div>
</div>`
)
export class SectionComponent 

当我点击 ToolBarComponent 时,我想将 TextComponent 添加到具有“活动”类的 SectionComponent。

【问题讨论】:

创建一个包含主题(或 BehaviourSubject)的服务,在 ToolbarComponent 中通过点击事件创建 Observable,从服务订阅和 next 到该主题。在添加类active 的地方,next 到服务中的其他主题(刚才next 的值是对该元素/组件的引用),因此您不需要查询 DOM。在 ViewComponent 中使用componentFactoryResolver 动态创建组件。通过ng-template 订阅主题和附加组件并添加指令。您可能可以动态创建ng-template somedirectiveRef,而不是在每个 html 你能帮我一个完整的答案吗?请。实际上“活动”类不是必需的,我确实想将组件添加到屏幕上最可见的部分。 :D - 喜欢这个问题:***.com/questions/38360676/… 请出示您更新后的代码。如果不需要动态占位符 - 只需在 HTML 中进行硬编码 - &lt;div app-type="section" id="SECTION1" class="active"&gt; &lt;ng-template myDirectiveThatReferencesTemplateComponentForInsertion&gt;&lt;/ng-template&gt;&lt;/div&gt; 【参考方案1】:

section.component.ts 上公开viewContainerRef

@Component(
   selector: 'div[app-type=section]',
   template: ''
)
export class SectionComponent 
  @Input() active: boolean;

   constructor(public viewContainerRef: ViewContainerRef)  
 

将输出添加到 toolbar.component.ts

@Component(
  selector: 'app-toolbar',
  template: '<button (click)="addComponentClick.emit()">Add Text component</button>'
)
export class ToolbarComponent 
  @Output() addComponentClick = new EventEmitter();
   constructor()  
 

view.component.ts 中为 TextComponents 创建一个 ComponentFactory 以将它们动态添加到 active SectionComponents:

import  Component, AfterViewInit, ViewChildren, QueryList, ElementRef, ComponentFactoryResolver, ComponentFactory, OnInit  from '@angular/core';
import  TextComponent  from './text.component';
import  SectionComponent  from './section.component';

@Component(
   selector: 'app-view',
   template: `<div class="container">
<app-toolbar (addComponentClick)="onAddComponentClick()"></app-toolbar>
<div app-type="section" id="SECTION1" [active]="true"></div>
<div app-type="section" id="SECTION2"></div>
</div>`
)
export class ViewComponent implements AfterViewInit, OnInit 
  @ViewChildren(SectionComponent) sections: QueryList<SectionComponent>;
  activeSections: SectionComponent[];
  textComponentFactory: ComponentFactory<TextComponent>;

  constructor(private componentFactoryResolver: ComponentFactoryResolver)   

  ngOnInit() 
    this.textComponentFactory = this.componentFactoryResolver.resolveComponentFactory(TextComponent);
  

  ngAfterViewInit() 
    this.activeSections = this.sections.reduce((result, section, index) => 
      if(section.active) 
        result.push(section);
      
      return result;
    , []);
  

   onAddComponentClick() 
    this.activeSections.forEach((section) => 
      section.viewContainerRef.createComponent(this.textComponentFactory);
    );
   

StackBlitz example

【讨论】:

如何访问所有 TextComponent "__id" 变量?stackblitz.com/edit/…提前致谢【参考方案2】:

我会使用 ngFor view.component.ts:

@Component(
   selector: 'app-view',
   template: `
     <div class="container">
       <app-toolbar (addEvent)="addEvent($event)"></app-toolbar>
       <div app-type="section" id="SECTION1" class="active">
          <app-text *ngFor="let appText in textArray"></app-text>
       </div>
       <div app-type="section" id="SECTION2"></div>
     </div>
   `
)
export class SectionComponent 
   public textArray: string[] = [];
   public addEvent(event: string) : void 
      textArray.push(event);
   
   ....

toolbar.component.ts:

import  Component, EventEmitter, Output  from '@angular/core';
@Component(
  selector: 'app-toolbar',
  template: '<button (click)="addNewText()">Add Text component</button>'
)
export class ToolbarComponent 
   @Output addEvent: EventEmitter<string> = new EventEmitter();
   constructor()  
   addNewText(): void 
      this.addEvent.emit("");
   
   ....
  

【讨论】:

感谢帮助,但我想使用组件。 “TextComponent”只是我的例子。我有许多相同的组件。例如:“HeadingH1Component”、“ListULComponent”、“ImageImgComponent”...【参考方案3】:

我还有其他方法,请检查你的情况

html
<ng-container *ngComponentOutlet="COMPONENT"></ng-container>

.ts
import  COMPONENT  from './..dir../component'

html
<ng-container *ngComponentOutlet="option.component"></ng-container>

.ts
import  COMPONENT  from './..dir../component'    
option = 
  component: COMPONENT

或者如果是动态的,将组件添加到entryComponent

app.module.ts
import  COMPONENT  from './..dir../component'
@NgModule(
   declarations: [COMPONENT],
   /.
   ..
   ..
  ./
  entryComponents: [COMPONENT]
);    

html
<ng-container *ngComponentOutlet="option.component"></ng-container>

.ts
/** The COMPONENT is not imported since it is added to entrycomponent**/
@Input() option;

【讨论】:

以上是关于以角度 2/4 动态添加组件的主要内容,如果未能解决你的问题,请参考以下文章

如何在角度2中显示动态添加的组件列表

角度 5 - 如何从 dom 中删除动态添加的组件

如何在没有预先确定的 ViewContainerRef 的情况下向 DOM 动态添加角度组件?

从角度组件获取数据

如何以角度动态删除组件

如何以角度添加带有字符串插值的html元素?