Need to insert Script tag in angular 2
Asked Answered
W

5

27

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.

Whichever answered 25/2, 2017 at 16:7 Comment(5)
As the answer already says, it's not an Angular problem, therefore it's also not related to DomSanitizer.Tarantass
What's the actual problem you're trying to solve? Why do you think you need to include a script tag in a template?Lawyer
Then when, why and how would one use bypassSecurityTrustScript?Whichever
@Lawyer - Essentially 2 trusted internal web projects that could be on various versions but allow injection of elements between them. Perhaps an easier way of putting it is I can establish conventions and protocols between 2 sites that want to share "components". I'd rather not do it with an iframe, though that may be my only option.Whichever
This is how you do it using Angular Pipe #39473549Propane
G
38

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>
Godderd answered 4/7, 2017 at 11:19 Comment(8)
This is helpful. One issue with this though is that in JS you often use the "{" character which angular will throw errors about when parsing the template. ngNonBindable seems like it would be the answer but there's an open issue where ngNonBindable doesn't handle unescaped "{" characters.Prototherian
Thanks for mentioning that. I haven't come across it yet (or can't remember). One solution would be to use a separate js file then. I use this for including javascript that does not have ts definitions but I think it is a bad practice :DGodderd
Why is this considered bad practice? Loading a script dynamically is sometimes the only option, for example, if I want to use Bing Maps in my application.Corpus
It's considered bad practice because MVC frameworks are made for separating logic (code) and GUI elements so you can replace either the view or the code with something else. If you look the scripts in your component you can replace Bing Maps with Google Maps or OpenStreetView with minimal effort. The situation you are talking about is the only case where it is allowed ;) If you want to do it really neatly you should fill the content of your script tag from the backend.Godderd
This works great. You can escape a { by replacing it with {{ '{' }}.Duplessis
This is working for me but I'm having the problem that the script does not load on the first page load. It works after I hit reload. Any tips?Nidia
@Nidia that looks like a caching issue. For me it works on the first page load.Godderd
I want to add that I made a mistake in my previous comment implying Angular is an MVC framework while in fact it is has the properties of MVVM (two-way binding between view and component, which acts like the viewmodel)Godderd
W
7

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?

Whichever answered 26/2, 2017 at 15:51 Comment(1)
Please provide context for links. Information hosted externally may disappear without warning.Bartlet
R
1

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]);
        }
      }
    }
Rafaello answered 11/1, 2018 at 17:32 Comment(0)
K
1

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

Kalie answered 23/2, 2019 at 0:37 Comment(0)
P
0

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.

Pargeting answered 6/9, 2019 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.