/ Angular

Binding HTML with Angular

In some cases, we may run into situations where an API returns HTML data, or we have some sort of HTML data in our code that we wish to display inside a <div> element in an Angular template.

In this scenario, applying the traditional binding by using the double curly brace syntax is not going to work as that will display 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 could produce the string <b>This text is bold</b> and this one is <i>italics</i> which is of course not what we wanted to achieve.

In order to display the correctly formatted HTML, we need to 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 will correctly yield "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 sometimes can contain malicious content - be wary of situations when unknown HTML data needs to be processed. Luckily, Angular comes with built-in XSS protection and a separate DomSanitizer which can be used to help prevent Cross-Site Scripting Security (XSS) bugs.

We can actually see the built-in XSS protection at work by looking at the following 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 should be presented that states 'sanitizing HTML stripped some content'. And the result should be that the alert message is not displayed at all. In fact, if we check the source code we should see something similar to this:

<div _ngcontent-c0=""><b>This text is bold</b> and this one is <i>italics</i> <img src="x"></div>

Note that the img tag is still present but there's no onerror attribute attached to it.

What if I have trusted content?

The default behaviour of the sanitizer can be overwritten by way of the DomSanitizer that we mentioned earlier. We can create a custom pipe and apply that pipe to HTML content that we deem to be safe.

Let's see how the pipe would look like:

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 that was mentioned earlier and no longer applies checking of the HTML input.

Let's apply this newly created pipe and see the result:

import { SanitizeHtmlPipe } from './sanitize-html.pipe';
// ...
template: `<div [innerHTML]="data | sanitizeHtml"></div>`

Notice that this time the alert() will appear so apply caution when using any of the bypassSecurityX methods in Angular.

A word of caution

This feature should not be changed as the default sanitizer does a big favour for us by removing unsafe HTML content. In other words, think about this for a really really long time and decide whether the HTML content can really be trusted.

Tamas Piros

Tamas Piros

Tamas is a full stack web developer turned technical trainer who has a decade of experience working with prestigious organisations and has delivered training classes all over the world.

Read More