在Angular 2/4中动态添加组件

3

我可以动态地添加组件吗?

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添加到具有“active”类的SectionComponent中。

2
在服务中创建一个带有Subject(或BehaviorSubject)的服务,在ToolbarComponent组件中从点击事件中创建一个Observable,订阅并从服务中的那个Subject进行“next”操作。在添加“active”类的位置,向服务中的另一个Subject进行“next”操作(刚才的“next”值是对该元素/组件的引用),这样您就不需要查询DOM了。在ViewComponent中使用componentFactoryResolver动态创建组件。订阅两个Subject,并通过ng-template和添加到它的指令将组件附加到模板上。您可以动态地创建ng-template somedirectiveRef而不是在每个HTML上创建。 - Julius Dzidzevičius
你能帮我�供一个完整的答案�?其�"class active"并�是必�的,我想把组件添加到�幕上最��的部分。😀 - 就�这个问题:https://dev59.com/questions/sFoT5IYBdhLWcg3wngm1 - bui quang huy
请展示您的更新后的代码。如果不需要动态占位符,请在HTML中直接硬编码 - <div app-type="section" id="SECTION1" class="active"> <ng-template myDirectiveThatReferencesTemplateComponentForInsertion></ng-template></div> - Julius Dzidzevičius
3个回答

10

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 中创建一个 ComponentFactory ,以便将 TextComponents 动态添加到 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实例


我该如何访问所有TextComponent "__id"变量?https://stackblitz.com/edit/so-dynamic-component-to-child-injywm?file=app%2Ftext.component.ts 预先感谢。 - Libu Mathew

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" ... - bui quang huy

2
我有另一种做法,请检查是否适用于你的情况。
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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接