Dynamically Execute Script in an Angular Component

Do you need to embed a third party application in your Angular application, but the third party only provides a script tag integration? How do you get the script to execute and render only where you place a specific component? Follow along to see how.

It would be wonderful if we could just put a script tag directly in our Angular component template. But Angular strips script tags out as a security measure. And we should generally be thankful for the strict security Angular enforces. But, in this instance, we are going to have to work around Angular’s defenses.

The only way to accomplish this is by writing out a script element to the DOM at runtime. We want to write it to the component’s DOM so that the output of the script (in many cases an iframe) renders in the correct place.

Let’s write a Directive just in case we have to do this again in the future. We could put a bunch of inputs for handling attributes on the script tag and handling text nodes, but that isn’t very flexible and would result in some complicated @Input parameters. Let’s try with a template element approach. We can use pretty much any tag that Angular isn’t going to strip for our template element. So, we cannot use ng-template, ng-container, or script. But div, span, etc, will work fine. First let’s look at the setup of our Directive: (full code is at the end of the article)

@Directive({
selector: ‘[appDynamicScript]’,
})
export class DynamicScriptDirective implements OnInit {
constructor(
private el: ElementRef,
@Inject(DOCUMENT) private document: Document) {} ngOnInit() {
const templateEl = this.el.nativeElement.firstElementChild as HTMLElement;
if (templateEl) {
this.replaceDivWithScript(templateEl);
}
} …
}

In the constructor we need to get the ElementRef of the host DOM element so that we can read its contents and replace the template element inside the host element. And we are going to need the Document object to create some DOM items to be append into the host element’s DOM. (You should be able to use Renderer2 to accomplish the same thing if you need SSR.)

In ngOnInit we get the first child element of the host element. This is our template for the new script tag we will generate. Let’s take a look at what the HTML should look like for our component:

<div appDynamicScript>
<div
src=”http://some.script.com/thing.js”
my-custom-attr=”some value”
>
Some Text
</div>
</div>

Notice that the first div has our directive applied to it, appDynamicScript. The first child element of that div is our template. The elements shown are for demo purposes only. Your script will have specific attributes that are required by the provider. But, in the end, it’s most any element with some attributes on it. If required, you can have a text node inside the template element as shown in the example, but this is not required.

Now we need our directive to replace the template element with a new script element.

private replaceDivWithScript(templateEl: HTMLElement) {
const script = this.document.createElement(‘script’);

// see full code below for implementations
this.copyAttributesFromTemplateToScript(templateEl, script);
this.copyTemplateContentToScript(templateEl, script); templateEl.remove(); // add the new script element to the host div so the
// browser will execute it
this.el.nativeElement.appendChild(script);
}

The steps are simple:

Create a new script elementCopy the attributes from the template to the new script element.Copy the text node from the template to the new script element.Remove the templateAppend the new script element to the DOM of the host element.

Step 5 causes the browser to execute the script tag! Examining the DOM output of the component at runtime shows the child div of the template has been removed. What is there? Well, that depends on what your script does. Many scripts will replace the script tag with other DOM elements, such as an iframe.

Here is the full code for the solution:

https://medium.com/media/1b1be412f24ccd337adeb435849c1af7/href

Happy embedding!

Dynamically Execute Script in an Angular Component was originally published in ngconf on Medium, where people are continuing the conversation by highlighting and responding to this story.

Leave a Comment

Your email address will not be published. Required fields are marked *