Reputation: 946
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.
Upvotes: 27
Views: 71460
Reputation: 3548
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]);
}
}
}
Upvotes: 1
Reputation: 741
Include jQuery in the JavaScript Console
Use the following above to solve your problem. It includes JQuery by inserting script tags into the DOM but it can work for any tag or javascript library that you desire. You can use the innerHTML attribute to customize the contents of the tag if you wish.
Upvotes: 0
Reputation: 2731
As per Angular documentation, the <script> element is forbidden and cannot be used in templates. <script> is ignored when used in templates.
Check here. Angular security
Upvotes: 1
Reputation: 781
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>
Upvotes: 38
Reputation: 946
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?
Upvotes: 7