86

My problem, that when I use innererHtml binding - angular2 remove all styles attributes. It's important for me, bacause in my task - html is generated on server-side with all styles. Example:

@Component({
  selector: 'my-app',
  template: `
    <input type="text" [(ngModel)]="html">
    <div [innerHtml]="html">
    </div>
  `,
})
export class App {
  name:string;
  html: string;
  constructor() {
    this.name = 'Angular2'
    this.html = "<span style=\"color:red;\">1234</span>";
  }
}

But in DOM I see only 1234 and this text is not red.

http://plnkr.co/edit/UQJOFMKl9OwMRIJ38U8D?p=preview

Thank you!

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
SamProf
  • 1,036
  • 1
  • 7
  • 8

3 Answers3

196

You can leverage DomSanitized to avoid it.

The easiest way is to create custom pipe like:

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

@Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform  {
  constructor(private sanitized: DomSanitizer) {}
  transform(value) {
    return this.sanitized.bypassSecurityTrustHtml(value);
  }
}

So you can use it like:

<div [innerHtml]="html | safeHtml"></div>

Plunker Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • 2
    Awesome solution! The only thing I've changed is I added `pure: true` to the `@Pipe` decorator, so that Angular knows not to recalculate it over and over again on every change detection, and does that only if `html` (the input) is changed. At least that's what I figure should be happening :) – evilkos Feb 14 '18 at 21:09
  • This is really good, however I have one question. I am using Angular Material and for some reason when I try to use Angular Material it doesn't work. For example, if I pass Test It doesn't actually give me a toolbar, just the text. Any idea on how to get around that? Thanks – DevShadow Apr 03 '18 at 21:03
  • @DevShadow Angular doesn't parse template at runtime. If you want to do that then see https://stackoverflow.com/questions/38888008/how-can-i-use-create-dynamic-template-to-compile-dynamic-component-with-angular – yurzui Apr 03 '18 at 21:06
  • 1
    This can open you up to a XSS attack. – Boomer Rogers Oct 09 '19 at 16:47
  • 4
    Bypassing sanitizer to trust any content can be a security concern. Since Angular is not a dedicated sanitizing library, it is overzealous towards suspicious content to not take any risks. It removed almost all attributes, for example. You can delegate sanitizing to a dedicated library — DOMPurify. Here's a wrapper library I've made to easily use DOMPurify with Angular: https://github.com/TinkoffCreditSystems/ng-dompurify – waterplea Nov 21 '19 at 07:48
  • 2
    This creates security concern. Before implementing this solution, please read @a darren answer! – Tu.Ma. Jan 08 '20 at 11:22
  • Awesome! In my case, making ViewEncapsulation.None did not work. But yours solution was spot on. Thanks – Swapnil Sourabh Aug 21 '20 at 14:59
  • 3
    the pipe name should be `unsafeHtml` since it will literally let unsafe html to the dom. There is nothing `safe` in this pipe – Tuomas Valtonen May 03 '21 at 13:59
51

I improved the example of yurzui a bit by completing the needed imports:

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

@Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform  {
  constructor(private sanitized: DomSanitizer) {}
  transform(value) {
    return this.sanitized.bypassSecurityTrustHtml(value);
  }
}

I also had to add the class in my app.module.ts file

import ...
import {SafeHtmlPipe} from "./pipes/safehtml.pipe";
@NgModule({
    declarations: [
        AppComponent,
        ...,
        SafeHtmlPipe  <--
    ],
    imports: [...],
    providers: [...],
    bootstrap: [AppComponent]
})
export class AppModule {
}
alastairtree
  • 3,960
  • 32
  • 49
mvermand
  • 5,829
  • 7
  • 48
  • 74
15

Note that the sanitizer has a few methods for trusting content e.g.

this.sanitizer.bypassSecurityTrustStyle(value);
this.sanitizer.bypassSecurityTrustHtml(value);
this.sanitizer.bypassSecurityTrustXxx(value); // - see docs [1]

via https://stackoverflow.com/a/41089093/142714

So, bypassSecurityTrustStyle may also be what you want here, as this will show inline styles within your HTML content (value).

[1] docs: https://angular.io/api/platform-browser/DomSanitizer

Darren Shewry
  • 10,179
  • 4
  • 50
  • 46
  • 2
    This should be the accepted answer in my opinion. only using `bypassSecurityTrustHtml()` is prune to run malicious javascript. If only styles are what you need to bypass, bypass using `bypassSecurityTrustStyle()` is better. – John Apr 10 '18 at 08:12
  • 7
    This should not be the accepted answer, as it is wrong. `bypassSecurityTrustStyle` returns `SafeStyle` and `[innerHTML]` requires `SafeHtml`. If you use it, it throws: `Required a safe HTML, got a Style`. So if you have html content with inline styles a combination of an external sanitizer and `bypassSecurityTrustHtml()` like described in below comment (https://stackoverflow.com/questions/39628007/angular2-innerhtml-binding-remove-style-attribute/39630507#comment104193367_39630507) should be the way to go. – Friedrich Sep 01 '20 at 11:41