Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2: Insert a dynamic component as child of a container in the DOM

Tags:

angular

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.

like image 899
Antoine OL Avatar asked Jun 29 '16 08:06

Antoine OL


1 Answers

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.

like image 168
Antoine OL Avatar answered Sep 21 '22 19:09

Antoine OL