toms
toms

Reputation: 515

Angular 2+ - Inject HTML content to component in Bootstrap Modal

I'm using Angular 5, and ng-bootstrap, to create a modal template as demonstrated here

I've decided to create a component as template for the modal and named it ModalComponent with the intention of creating it dynamically and passing content to it from the parent component:

constructor(private modalService: NgbModal){}

onOpenModal(){
  this.modalService.open(ModalComponent);
}

I've managed to trigger the modal to open through the parent component, and I understand how to pass variables to the modal component in the same was that is seen in this question+answer.

However, I wanted to pass some HTML content to the ModalComponent as a variable, and then output it in it's template. Essentially I would like to achieve something similar to how <app-my-component> some html </app-my-component> injects 'some-html'into the child component's <ng-content></ng-content>.

I'm sure there is a simple way to do it with dynamically created components, but I am having trouble figuring how to do that, and I understand the DOM manipulation should be done with care and preferably with renderer2.

Upvotes: 0

Views: 1711

Answers (1)

diopside
diopside

Reputation: 3062

You could always give the modal component an input of a 'TemplateRef' element.

Import ViewChild, ViewContainerRef and TemplateRef into your modal component if you haven't already

 import { ..., Input, ViewChild, TemplateRef, ViewContainerRef } from '@angular/core'

In your modal component, somewhere in the template you would create an 'anchor' element where you want to inject the template input (or just add a #'d template variable name to an existing element)

<div #anchorElement></div>

Now down in the component, declare your new input and a ViewChild object for the anchorElement

@Input() inputTemplate : TemplateRef<any>;
@ViewChild('anchorElement', {read:ViewContainerRef}) anchor : ViewContainerRef;

Now in the parent component you could create an ng-template element, give it a template variable name so you can reference it, and send it to the modal component as the inputTemplate input

<ng-template #myTemplate> ...whatever HTML here... </ng-template>

<modal-component [inputTemplate]="myTemplate"></modal-component>

Now you are sending whatever is inside that ng-template element as an input to your modal component. To actually render the template, you need to wait until the ngAfterViewInit lifecycle hook, and use the 'create embedded view' method of the ViewContainerRef to inject it.

In the modal component:

 ngAfterViewInit() {
     this.anchor.createEmbeddedView(this.inputTemplate);
 }

If you ever need to switch it out or remove it, you can call the 'remove' method of the viewContainerRef

this.anchor.remove()

The downside (if you think of it like that) of this method is you're limited to injecting template HTML that has been processed by the Angular compiler, so you can't pull arbitrary external HTML files in and inject those as a template with this method (unless you manually use the angular compiler on them after pulling them in, but that is complicated). Your templates would have to be somewhere in your app so that they can be compiled by Angular. You have a lot of freedom where they are in your app though. Sometimes I make a component that basically just has a ton of different ng-template elements, and I give them each a directive that uploads their TemplateRef to a singleton service which stores them in a map. Then if I ever need those templates again in my app, I can simply inject that singleton service and access whichever template I need from it. Ng-template elements won't ever be rendered until they are explicitly embedded into a view container (otherwise they are processed as comments) so you can put that master template component anywhere really (I usually put it in or near the root of my app to guarantee its quick availability)

Upvotes: 3

Related Questions