Need to insert Script tag in angular 2
I've already done a bit of reading and searching and pretty much everything I find points to that a script tag cannot be included in a template in Angular 2.
we are removing tags from templates on purpose as you shouldn't use those to load code on demand. https://github.com/angular/angular/issues/4903 [2015]
However - there is a function bypassSecurityTrustScript
I'd like to know when and how bypassSecurityTrustScript
in Angular 2 is intended to be used?
I know a similar question has been asked:
Angular2 dynamically insert script tag - though no one answered their question of how to use bypassSecurityTrustScript
, and I'm not sure how the provided answer to that question could even work as it appears to use JavaScript within a template.
Having scripts in your views is usually a bad practice. If you insist on doing this you can use this component:
scripthack.component.html:
<div #script style.display="none">
<ng-content></ng-content>
</div>
scripthack.component.ts:
import { Component, ElementRef, ViewChild, Input } from '@angular/core';
@Component({
selector: 'script-hack',
templateUrl: './scripthack.component.html'
})
export class ScriptHackComponent {
@Input()
src: string;
@Input()
type: string;
@ViewChild('script') script: ElementRef;
convertToScript() {
var element = this.script.nativeElement;
var script = document.createElement("script");
script.type = this.type ? this.type : "text/javascript";
if (this.src) {
script.src = this.src;
}
if (element.innerHTML) {
script.innerHTML = element.innerHTML;
}
var parent = element.parentElement;
parent.parentElement.replaceChild(script, parent);
}
ngAfterViewInit() {
this.convertToScript();
}
}
usage (inline):
<script-hack>alert('hoi');</script-hack>
usage (external):
<script-hack src="//platform.twitter.com/widgets.js" type="text/javascript"></script-hack>
Turns out I was thinking about this a bit wrong. I was trying to find a way to put the script into the template by using standard Angular template variables. When Angular populates the template, it cleanses the values and so the script tags are lost.
I managed to finally get the script tags in following this article: https://netbasal.com/angular-2-security-the-domsanitizer-service-2202c83bd90#.7njysc6z1
That then left me with the problem described by: Angular2 dynamically insert script tag
I then moved the logic into the component class based on this: Where does DOM manipulation belong in Angular 2?
I had a similar use case, where I don't know if the HTML will contain a script tag. Since HTML5 doesn't execute the script if it's part of an innerHTML assignment, I used a slightly different approach.
This is part of a plugin system, so I needed to be able to add html+scripts on demand.
Source here - https://github.com/savantly-net/sprout-platform/blob/development/web/sprout-web-ui/src/app/dynamic/dynamic.component.ts
import { Component, Input, AfterViewInit, ViewChild, Directive, ElementRef } from '@angular/core';
@Directive({
/* tslint:disable-next-line:directive-selector */
selector: 'dynamic-directive'
})
export class DynamicDirective {}
@Component({
template: `<dynamic-directive></dynamic-directive>`
})
export class DynamicComponent implements AfterViewInit {
@Input() body: any;
@ViewChild(DynamicDirective, {read: ElementRef}) dynamic: ElementRef;
constructor() { }
// loads all the html from the plugin, but removes the script tags and appends them individually,
// since html will not execute them if they are part of the innerHTML
ngAfterViewInit(): void {
const div = document.createElement('div');
div.innerHTML = this.body;
const scriptElements = [];
const scriptNodes = div.querySelectorAll('script');
for (let i = 0; i < scriptNodes.length; i++) {
const scriptNode = scriptNodes[i];
// Create a new script element so HTML5 will execute it upon adding to DOM
const scriptElement = document.createElement('script');
// Copy all the attributes from the original script element
for (let aI = 0; aI < scriptNode.attributes.length; aI++) {
scriptElement.attributes.setNamedItem(<Attr>scriptNode.attributes[aI].cloneNode());
}
// Add any content the original script element has
const scriptContent = document.createTextNode(scriptNode.textContent);
scriptElement.appendChild(scriptContent);
// Remove the original script element
scriptNode.remove();
// add the new element to the list
scriptElements.push(scriptElement);
}
this.dynamic.nativeElement.appendChild(div);
// Finally add the new script elements to the DOM
for (let i = 0; i < scriptElements.length; i++) {
this.dynamic.nativeElement.appendChild(scriptElements[i]);
}
}
}