Binding HTML with Angular
Older Article
This article was published 8 years ago. Some information may be outdated or no longer applicable.
Sometimes an API returns HTML data, or you’ve got HTML sitting in your code that you want to render inside a <div> in an Angular template.
Using the traditional double curly brace syntax won’t work here. It displays the encoded version of the HTML string:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
styleUrls: ['./app.component.css'],
template: `<div>{{ data }}</div>`,
})
export class AppComponent {
data = `<b>This text is bold</b> and this one is <i>italics</i>`;
}
The above spits out the string <b>This text is bold</b> and this one is <i>italics</i>, which is obviously not what we want.
To display correctly formatted HTML, bind to the div’s innerHTML property:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
styleUrls: ['./app.component.css'],
template: `<div [innerHTML]="data"></div>`,
})
export class AppComponent {
data = `<b>This text is bold</b> and this one is <i>italics</i>`;
}
The above correctly yields “This text is bold and this one is italics.”
Please note that by default Angular is sanitizing the input for templates and it escapes untrusted values.
Safe HTML
HTML data can contain malicious content. Be wary when unknown HTML needs processing. Angular ships with built-in XSS protection and a separate DomSanitizer that helps prevent Cross-Site Scripting Security (XSS) bugs.
We can see the built-in XSS protection at work with this example:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
styleUrls: ['./app.component.css'],
template: `<div [innerHTML]="data"></div>`,
})
export class AppComponent {
data = `<b>This text is bold</b> and this one is <i>italics</i> <img src=x onerror='alert("hello there")'>`;
}
When serving the application, a warning message states ‘sanitizing HTML stripped some content.’ The alert never fires. Checking the source code reveals something like this:
<div _ngcontent-c0="">
<b>This text is bold</b> and this one is <i>italics</i> <img src="x" />
</div>
Note the img tag is still there but the onerror attribute has been stripped out.
What if I have trusted content?
You can override the default sanitiser behaviour using the DomSanitizer mentioned earlier. Create a custom pipe and apply it to HTML content you know to be safe.
Here’s the pipe:
import { Pipe, PipeTransform, SecurityContext } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({
name: 'sanitizeHtml',
})
export class SanitizeHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(value: any): any {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
}
This pipe bypasses the built-in security and no longer checks the HTML input.
Apply the pipe and see the result:
import { SanitizeHtmlPipe } from './sanitize-html.pipe';
// ...
template: `<div [innerHTML]="data | sanitizeHtml"></div>`;
This time the alert() fires. Apply caution when using any of the bypassSecurityX methods in Angular.
A word of caution
Don’t change this default behaviour lightly. The built-in sanitiser does you a massive favour by stripping unsafe HTML content. Think long and hard about whether the HTML content can truly be trusted before bypassing it.