Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 how to execute script tag from innerHTML

I have load the HTML page by http.get() method, and i add content this page to div tag.

getRequestToAssignPage (param: string) : any {

    return this.$http.get(param)
        .map((res: Response) => {

            this.status = res;

            return res.text();
        })
        .toPromise()
        .then(response => {



            let restr: string = response;



            restr = restr.replace(/(<head[^>]*)(?:[^])*?\/head>/ig, '')
                .replace(/(<(\/?)body([^>]*?)>)/g, '')
                .replace(/(<style[^>]*)(?:[^])*?\/style>/g, '')
                .replace(/(<(\/?)html([^>]*?)>)/g, '')
                .replace(/(<app-root[^>]*)(?:[^])*?\/app-root>/ig, '')
                .replace(/(<\?[\s\S]*?\?>)|(<!DOCTYPE\s+\w+\s+\[[\s\S]*?\]>)|(<!\w[\s\S]*?>)/g, '')
                .replace(/(href\s*=\s*(?:"))/ig, 'href="/#')
                .replace(/(href\s*=\s*(?:'))/ig, "href='/#");



            this.response = restr;


        })
        .catch(error => this.status = error );

}

How you do you see, this method, put response in variable, and parse string by regular expressions Ok, and next I add it to div, like this

<div [innerHTML]="response | safe"></div>

Good, my page is display. But, scripts doesn't work. They are exist in the div tag, but doesn't execute.

I had tried do that with eval() but this finished by poorly

let scripts: string[] = restr.match(/\<scr[\s\S]*?ipt>/g);

            this.srcfield.nativeElement.innerHTML = '';

            scripts.forEach((value, index) => {
                eval.call(null, (this.srcfield.nativeElement.innerHTML = value));
            });

SyntaxError: Unexpected token <

Why innerHTML doesn't execute loaded script tags? How i can fix that?

like image 769
haacki47 Avatar asked Jan 09 '17 11:01

haacki47


People also ask

Can scripts be inserted with innerHTML?

Security considerations HTML specifies that a <script> tag inserted with innerHTML should not execute. For that reason, it is recommended that instead of innerHTML you use: Element.

Can I use innerHTML in angular?

Angular 14 provides a [innerHTML] property binding that you can use to render HTML instead of using interpolation, which will, as you might have noticed, render any HTML string as a plain text.

Can I use script tag in angular HTML?

AngularJS is a JavaScript framework. It can be added to an HTML page with a <script> tag.

How are script tags executed?

The <script> tag is used to embed a client-side script (JavaScript). The <script> element either contains scripting statements, or it points to an external script file through the src attribute. Common uses for JavaScript are image manipulation, form validation, and dynamic changes of content.


2 Answers

Based on the Adam's solution you can implement a custom directive along with the pipe and re-insert scripts into the DOM. This would account for both cases: inline scripts and "src" scripts. Keep in mind that allowing scripts like so is very dangerous.

Pipe:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({ name: 'safeHtml' })
export class SafeHtmlPipe implements PipeTransform {
    constructor(private sanitizer: DomSanitizer) { }

    transform(html) {
        return this.sanitizer.bypassSecurityTrustHtml(html);
    }
}

Directive:

import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({ selector: '[runScripts]' })
export class RunScriptsDirective implements OnInit {
    constructor(private elementRef: ElementRef) { }
    ngOnInit(): void {
        setTimeout(() => { // wait for DOM rendering
            this.reinsertScripts();
        });
    }
    reinsertScripts(): void {
        const scripts = <HTMLScriptElement[]>this.elementRef.nativeElement.getElementsByTagName('script');
        const scriptsInitialLength = scripts.length;
        for (let i = 0; i < scriptsInitialLength; i++) {
            const script = scripts[i];
            const scriptCopy = <HTMLScriptElement>document.createElement('script');
            scriptCopy.type = script.type ? script.type : 'text/javascript';
            if (script.innerHTML) {
                scriptCopy.innerHTML = script.innerHTML;
            } else if (script.src) {
                scriptCopy.src = script.src;
            }
            scriptCopy.async = false;
            script.parentNode.replaceChild(scriptCopy, script);
        }
    }
}

Usage:

<div [innerHTML]="myDynamicMarkup | safeHtml" runScripts></div>
like image 86
Pavel Shchegolevatykh Avatar answered Sep 28 '22 01:09

Pavel Shchegolevatykh


In your template, you can set up something like:

<div #htmlContainer [innerHTML]="trustedHTML"></div>

In the corresponding component, you can load your HTML into your template, build up an array of script tags, and eval each one:

@ViewChild('htmlContainer') container;

ngOnInit() {
  this.http.get('html-file.html').subscribe((res: Response) => {
    this.trustedHTML = this.sanitizer.bypassSecurityTrustHtml(res.text());

    setTimeout(() => { //wait for DOM rendering
      let scripts = this.container.nativeElement.getElementsByTagName('script');
      for (let script of scripts){
        eval(script.text)
      }
    })

  });

I hate that I have to resort to using setTimeout and eval for something so seemingly simple, but it works.

...but not for <script src=".... I ended up importing jQuery and using $(parent).load(url);. If someone has a pure Angular solution, please post it.

like image 38
adamdport Avatar answered Sep 28 '22 01:09

adamdport