My goal is to create a child component and insert into the parent component template. There are examples to do this. However, I create parent component template (DOM
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) : ComponentRef {
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).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).rootNodes[0]);
You can use it as follows:
@Component({
selector: 'my-app',
template: ` `,
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, 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