Angular 2
Lägg dynamiskt till komponenter med ViewContainerRef.createComponent
Sök…
En omslagskomponent som lägger till dynamiska komponenter deklarativt
En anpassad komponent som tar typen av en komponent som input och skapar en instans av den komponenttypen i sig själv. När ingången uppdateras tas den tidigare tillagda dynamiska komponenten bort och den nya läggs till i stället.
@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();
}
}
}
Detta gör att du kan skapa dynamiska komponenter som
<dcl-wrapper [type]="someComponentType"></dcl-wrapper>
Lägg dynamiskt till komponent på en specifik händelse (klicka)
Huvudkomponentfil:
//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 {}
Återges dynamiskt skapade komponentuppsättning på mall html i Angular2
Vi kan skapa dynamisk komponent och få instansen av komponent i en matris och slutligen återges den på mall.
Vi kan till exempel överväga två widgetkomponenter, ChartWidget och PatientWidget som utökade klassen WidgetComponent som jag ville lägga till i behållaren.
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 (med 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 (samma som diagram-widget med Primeng Panel)
WidgetComponent.ts
@Component({
selector: 'widget',
template: '<ng-content></ng-content>'
})
export class WidgetComponent{
}
vi kan skapa dynamiska komponentförekomst genom att välja de befintliga komponenterna. Till exempel,
@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;
}
}
Slutligen använder vi det i app-komponenten. 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
Vissa ändringar av @yurzui för att använda mushändelse på widgets
view.directive.ts
import {ViewRef, Direktiv, Input, ViewContainerRef} från '@ vinkel / kärna';
@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