8

I'm getting from server content into json object field, where it is html, <style></style> and <script></script> tags, and I want to execute it like this:

[innerHtml]="content | sanitize", but <script></script> tags do not execute. Is it possible to make it work?

My sanitize pipe looks like this:

import {Pipe} from '@angular/core';
import {DomSanitizationService} from '@angular/platform-browser';

@Pipe({
    name: 'sanitize',
    pure: true
})
export class Sanitize {
    constructor(private sanitizer: DomSanitizationService) {

    }

    transform(html: string) {

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

I know, that there is bypassSecurityTrustScript function in DomSanitizationService, but how can I use it in my case?

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Andrey
  • 311
  • 2
  • 8
  • 29

3 Answers3

27

It's not an angular 2's problem, script tags inserted via innerHTML are not executed.

If you have html string that contains script tags insert it this way:

const fragment = document.createRange().createContextualFragment(yourHtmlString);
anyElement.appendChild(fragment);
Alex Sokolov
  • 311
  • 3
  • 6
  • 1
    Worked perfectly – iniravpatel Dec 20 '17 at 12:26
  • For others that may need to add a script to the body at some point, but intentionally don't want it bootstrapped on initial page load with the rest of the app: `document.body.appendChild(fragment)` could also work on the second line of Alex's code rather than setting up a ViewChild in Angular. – jmq Jan 10 '18 at 15:14
2

This will solve the issue...

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

@Pipe({
  name: 'sanitize',
  pure: true
})
export class Sanitize {

  constructor(private domSanitizer: DomSanitizer) { }


  handleExternalScriptsInHtmlString(string) {
    let that = this;
    var parser = new DOMParser();
    var scripts = parser.parseFromString(string, 'text/html').getElementsByTagName('script');
    var results = [];

    for (var i = 0; i < scripts.length; i++) {
      var src = scripts[i].getAttribute('src');
      if (src.length && results.indexOf(src) === -1) {
        results.push(src);
        that.addScript(src);
      }
    }

    return results;
  }

  addScript(src) {
    var script = document.createElement('script');
    script.setAttribute('src', src);
    document.body.appendChild(script);
  }


  transform(htmlContent: any) {
    let sanitizeHtmlContent = this.domSanitizer.bypassSecurityTrustHtml(htmlContent);

    this.handleExternalScriptsInHtmlString(htmlContent);

    return sanitizeHtmlContent;
  }
}
Dhrumil Bhankhar
  • 1,301
  • 1
  • 14
  • 15
1

There is no angular way... You need to do it like this....

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

@Pipe({
  name: 'sanitize',
  pure: true
})
export class Sanitize {

  constructor(private domSanitizer: DomSanitizer) { }


  handleExternalScriptsInHtmlString(string) {
    let that = this;
    var parser = new DOMParser();
    var scripts = parser.parseFromString(string, 'text/html').getElementsByTagName('script');
    var results = [];

    for (var i = 0; i < scripts.length; i++) {
      var src = scripts[i].getAttribute('src');
      if (src.length && results.indexOf(src) === -1) {
        results.push(src);
        that.addScript(src);
      }
    }

    return results;
  }

  addScript(src) {
    var script = document.createElement('script');
    script.setAttribute('src', src);
    document.body.appendChild(script);
  }


  transform(htmlContent: any) {
    let sanitizeHtmlContent = this.domSanitizer.bypassSecurityTrustHtml(htmlContent);

    this.handleExternalScriptsInHtmlString(htmlContent);

    return sanitizeHtmlContent;
  }
}
Dhrumil Bhankhar
  • 1,301
  • 1
  • 14
  • 15
  • this has the same issue, right? because you still just get a variable that you'd have to display using innerHtml which doesn't execute script tags – SeanMC Apr 26 '18 at 03:58
  • @SeanKPS No this one will execute the script tags in fact. Notice the function addScript. We are manually finding script tags and explicitly adding them to DOM – Dhrumil Bhankhar Jun 21 '18 at 12:50
  • I thought angular wouldn't execute script tags at all -even if they're in the DOM. Maybe that's only within component templates and doesn't apply when appended to the body directly. Still, I needed to put the script output exactly where the script tag was in the DOM, for Github Gists, I ended up writing a couple functions to sort it out. – SeanMC Jun 21 '18 at 16:34