We can't create a ViewContainerRef
, as a ViewContainerRef
only exists within a view.
In 2.3.0, attachView
was introduced which allows you to be able to attach change detection to the ApplicationRef. You can create some class that will encapsulate your logic like:
export class HtmlContainer {
private attached: boolean = false;
private disposeFn: () => void;
constructor(
private hostElement: Element,
private appRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector) {
}
attach(component: Type<any>) : ComponentRef<any> {
if(this.attached) {
throw new Error('component has already been attached')
}
this.attached = true;
const childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
let componentRef = childComponentFactory.create(this.injector);
this.appRef.attachView(componentRef.hostView);
this.disposeFn = () => {
this.appRef.detachView(componentRef.hostView);
componentRef.destroy();
};
this.hostElement.appendChild((componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]);
return componentRef;
}
dispose() {
if(this.attached) {
this.disposeFn();
}
}
}
this class is just helper that
1) resolves your dynamic component
this.componentFactoryResolver.resolveComponentFactory(component);
2) then compiles component by calling compFactory.create
3) after that registers its changeDetector
(componentRef.hostView
extends ChangeDetectorRef
) by calling mentioned above appRef.attachView
(otherwise change detection won't work for your component)
4) and finally appends the rootNode of your component to the host element
this.hostElement.appendChild((componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]);
You can use it as follows:
@Component({
selector: 'my-app',
template: `<div id="myArea"></div> `,
entryComponents: [NewChildComponent]
})
export class AppComponent {
containers: HtmlContainer[] = [];
constructor(
private appRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector) {
}
ngOnInit() {
var myArea = document.getElementById('myArea');
var myRow = document.createElement("div");
myArea.appendChild(myRow);
this.addComponentToRow(NewChildComponent, myRow, 'test1');
this.addComponentToRow(NewChildComponent, myRow, 'test2');
}
addComponentToRow(component: Type<any>, row: HTMLElement, param: string) {
let container = new HtmlContainer(row, this.appRef, this.componentFactoryResolver, this.injector);
let componentRef = container.attach(component);
componentRef.instance.param1 = param;
this.containers.push(container);
}
ngOnDestroy() {
this.containers.forEach(container => container.dispose());
}
}
Plunker Example
See also
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…