Angular 2
Dynamisches Hinzufügen von Komponenten mithilfe von ViewContainerRef.createComponent
Suche…
Eine Wrapper-Komponente, die dynamische Komponenten deklarativ hinzufügt
Eine benutzerdefinierte Komponente, die den Typ einer Komponente als Eingabe übernimmt und eine Instanz dieses Komponententyps in sich selbst erstellt. Wenn die Eingabe aktualisiert wird, wird die zuvor hinzugefügte dynamische Komponente entfernt und stattdessen die neue hinzugefügt.
@Component({
selector: 'dcl-wrapper',
template: `<div #target></div>`
})
export class DclWrapper {
@ViewChild('target', {
read: ViewContainerRef
}) target;
@Input() type;
cmpRef: ComponentRef;
private isViewInitialized: boolean = false;
constructor(private resolver: ComponentResolver) {}
updateComponent() {
if (!this.isViewInitialized) {
return;
}
if (this.cmpRef) {
this.cmpRef.destroy();
}
this.resolver.resolveComponent(this.type).then((factory: ComponentFactory < any > ) => {
this.cmpRef = this.target.createComponent(factory)
// to access the created instance use
// this.cmpRef.instance.someProperty = 'someValue';
// this.cmpRef.instance.someOutput.subscribe(val => doSomething());
});
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}
So können Sie dynamische Komponenten wie
<dcl-wrapper [type]="someComponentType"></dcl-wrapper>
Dynamisches Hinzufügen einer Komponente zu einem bestimmten Ereignis (Klicken)
Hauptkomponenten-Datei:
//our root app component
import {Component, NgModule, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentRef} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {ChildComponent} from './childComp.ts'
@Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<input type="button" value="Click me to add element" (click) = addElement()> // call the function on click of the button
<div #parent> </div> // Dynamic component will be loaded here
</div>
`,
})
export class App {
name:string;
@ViewChild('parent', {read: ViewContainerRef}) target: ViewContainerRef;
private componentRef: ComponentRef<any>;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
this.name = 'Angular2'
}
addElement(){
let childComponent = this.componentFactoryResolver.resolveComponentFactory(ChildComponent);
this.componentRef = this.target.createComponent(childComponent);
}
}
childComp.ts:
import{Component} from '@angular/core';
@Component({
selector: 'child',
template: `
<p>This is Child</p>
`,
})
export class ChildComponent {
constructor(){
}
}
app.module.ts:
@NgModule({
imports: [ BrowserModule ],
declarations: [ App, ChildComponent ],
bootstrap: [ App ],
entryComponents: [ChildComponent] // define the dynamic component here in module.ts
})
export class AppModule {}
Dynamisch erstelltes Komponenten-Array für Template-HTML in Angular2
Wir können dynamische Komponenten erstellen und die Instanzen der Komponenten in ein Array bringen und schließlich als Vorlage darstellen.
Zum Beispiel können wir zwei Widgetkomponenten betrachten, ChartWidget und PatientWidget, die die Klasse WidgetComponent erweitert haben, die ich im Container hinzufügen wollte.
ChartWidget.ts
@Component({
selector: 'chart-widget',
templateUrl: 'chart-widget.component.html',
providers: [{provide: WidgetComponent, useExisting: forwardRef(() => ChartWidget) }]
})
export class ChartWidget extends WidgetComponent implements OnInit {
constructor(ngEl: ElementRef, renderer: Renderer) {
super(ngEl, renderer);
}
ngOnInit() {}
close(){
console.log('close');
}
refresh(){
console.log('refresh');
}
...
}
chart-widget.compoment.html (mit dem primeng Panel)
<p-panel [style]="{'margin-bottom':'20px'}">
<p-header>
<div class="ui-helper-clearfix">
<span class="ui-panel-title" style="font-size:14px;display:inline-block;margin-top:2px">Chart Widget</span>
<div class="ui-toolbar-group-right">
<button pButton type="button" icon="fa-window-minimize" (click)="minimize()"</button>
<button pButton type="button" icon="fa-refresh" (click)="refresh()"></button>
<button pButton type="button" icon="fa-expand" (click)="expand()" ></button>
<button pButton type="button" (click)="close()" icon="fa-window-close"></button>
</div>
</div>
</p-header>
some data
</p-panel>
DataWidget.ts
@Component({
selector: 'data-widget',
templateUrl: 'data-widget.component.html',
providers: [{provide: WidgetComponent, useExisting: forwardRef(() =>DataWidget) }]
})
export class DataWidget extends WidgetComponent implements OnInit {
constructor(ngEl: ElementRef, renderer: Renderer) {
super(ngEl, renderer);
}
ngOnInit() {}
close(){
console.log('close');
}
refresh(){
console.log('refresh');
}
...
}
data-widget.compoment.html (wie Chart-Widget mit dem primeng Panel)
WidgetComponent.ts
@Component({
selector: 'widget',
template: '<ng-content></ng-content>'
})
export class WidgetComponent{
}
Wir können dynamische Komponenteninstanzen erstellen, indem Sie die bereits vorhandenen Komponenten auswählen. Zum Beispiel,
@Component({
selector: 'dynamic-component',
template: `<div #container><ng-content></ng-content></div>`
})
export class DynamicComponent {
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;
public addComponent(ngItem: Type<WidgetComponent>): WidgetComponent {
let factory = this.compFactoryResolver.resolveComponentFactory(ngItem);
const ref = this.container.createComponent(factory);
const newItem: WidgetComponent = ref.instance;
this._elements.push(newItem);
return newItem;
}
}
Schließlich verwenden wir es in der App-Komponente. app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app/app.component.html',
styleUrls: ['./app/app.component.css'],
entryComponents: [ChartWidget, DataWidget],
})
export class AppComponent {
private elements: Array<WidgetComponent>=[];
private WidgetClasses = {
'ChartWidget': ChartWidget,
'DataWidget': DataWidget
}
@ViewChild(DynamicComponent) dynamicComponent:DynamicComponent;
addComponent(widget: string ): void{
let ref= this.dynamicComponent.addComponent(this.WidgetClasses[widget]);
this.elements.push(ref);
console.log(this.elements);
this.dynamicComponent.resetContainer();
}
}
app.component.html
<button (click)="addComponent('ChartWidget')">Add ChartWidget</button>
<button (click)="addComponent('DataWidget')">Add DataWidget</button>
<dynamic-component [hidden]="true" ></dynamic-component>
<hr>
Dynamic Components
<hr>
<widget *ngFor="let item of elements">
<div>{{item}}</div>
<div [innerHTML]="item._ngEl.nativeElement.innerHTML | sanitizeHtml">
</div>
</widget>
https://plnkr.co/edit/lugU2pPsSBd3XhPHiUP1?p=preview
Eine Änderung von @yurzui zur Verwendung des Mausereignisses in den Widgets
view.directive.ts
importiere {ViewRef, Direktive, Eingabe, ViewContainerRef} von '@ angle / core';
@Directive({
selector: '[view]'
})
export class ViewDirective {
constructor(private vcRef: ViewContainerRef) {}
@Input()
set view(view: ViewRef) {
this.vcRef.clear();
this.vcRef.insert(view);
}
ngOnDestroy() {
this.vcRef.clear()
}
}
app.component.ts
private elements: Array<{ view: ViewRef, component: WidgetComponent}> = [];
...
addComponent(widget: string ): void{
let component = this.dynamicComponent.addComponent(this.WidgetClasses[widget]);
let view: ViewRef = this.dynamicComponent.container.detach(0);
this.elements.push({view,component});
this.dynamicComponent.resetContainer();
}
app.component.html
<widget *ngFor="let item of elements">
<ng-container *view="item.view"></ng-container>
</widget>
https://plnkr.co/edit/JHpIHR43SvJd0OxJVMfV?p=preview