Is there a way to insert dynamically a component as a child (not a sibling) of a DOM tag in Angular 2?
There are plenty of examples around there to insert a dynamic component as a sibling of a given ViewContainerRef
's tag, like (as of RC3):
@Component({ selector: '...', template: '<div #placeholder></div>' }) export class SomeComponent { @ViewChild('placeholder', {read: ViewContainerRef}) placeholder; constructor(private componentResolver: ComponentResolver) {} ngAfterViewInit() { this.componentResolver.resolveComponent(MyDynamicComponent).then((factory) => { this.componentRef = this.placeholder.createComponent(factory); }); } }
But this generates a DOM similar to:
<div></div> <my-dynamic-component></my-dynamic-component>
Expected result:
<div> <my-dynamic-component></my-dynamic-component> </div>
Using the SomeComponent
's ViewContainerRef
has the same result, it is still inserting the generated component as a sibling, not a child. I would be okay with a solution where the template is empty and dynamic components are inserted in the template (within the component selector tag).
The DOM structure is very important when using libraries like ng2-dragula to drag from a list of dynamic components and benefit from the model updates. The extra div is in the list of draggable elements, but outside the model, breaking the drag & drop logic.
Some say it is not possible (c.f. this comment), but it sounds like a very surprising limitation.
TL;DR: replace <div #placeholder></div>
with <div><ng-template #placeholder></ng-template></div>
to insert inside the div
.
Here is a working stackblitz example (Angular 6), and the relevant code:
@Component({ selector: 'my-app', template: `<div><ng-template #container></ng-template></div>` }) export class AppComponent implements OnInit { @ViewChild('container', {read: ViewContainerRef}) viewContainer: ViewContainerRef; constructor(private compiler: Compiler) {} ngOnInit() { this.createComponentFactory(MyDynamicComponent).then( (factory: ComponentFactory<MyDynamicComponent>) => this.viewContainer.createComponent(factory), (err: any) => console.error(err)); } private createComponentFactory(/*...*/) {/*...*/} }
It seems <ng-container #placeholder></ng-container>
is also working (replace ng-template
by ng-container
). I like this approach because <ng-container>
is clearly addressed to this usecase (a container that don't add a tag) and can be used in other situations like NgIf
without wrapping in a real tag.
PS: @GünterZöchbauer directed me to the right discussion in a comment, and I finally answered my own question.
Edit [2018-05-30]: Updated to stackblitz link to have a working, up-to-date example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With