How to use Web Components in HTMLTemplateElement in Angular

patricksevat

Patrick Sevat

Posted on July 19, 2020

How to use Web Components in HTMLTemplateElement in Angular

TLDR

  1. Use [innerHTML] on the <template> tag
  2. Use DomSanitizer.bypassSecurityTrustHtml() to make sure your Custom Elements are not stripped from your provided HTML string

See code example at the end


Within the Rabobank Design System (based on Web Components) we sometimes come across with unusual specs. Our department relating to wholesale banking (large business customers) came up with the requirement of a select dropdown containing thousands of bank accounts.

We also envisaged different use cases than just thousands of bank accounts. Maybe thousands of checkboxes with labels and icons. To allow for future use cases we wanted to leverage the <template> component, also known as HTMLTemplateElement.

The cool thing about the <template> tag is that its contents are not actually rendered by the browser as long as they reside within <template>. This would give us the flexibility we need for our component.

Our components worked fine in plain HTML / JS but once we appended the components within our <template> tag to the DOM using Angular, it started double rendering! 😠

When inspecting the <template> element we also noticed that in Angular it did not yield a new DocumentFragment as it does in plain HTML...

This means the <template> tag was not recognized as such and because our Web Components used slots, those slots where rendered and then re-rendered upon appending to the DOM.

Unfortunately, searching Google for angular + template only yields results for ng-template, but after searching for Angular HTMLTemplateElement we got to this StackOverflow question, which point us to the [innerHTML] syntax.

After trying binding to the innerHTML property we noticed that the double rendering stopped, but the Web Components within the <template> tag were not rendering as they should, but with an example consisting of HTML5 elements (span, div, p...) it did render as expected.

There were two possible explanations:

  1. The Web Components were not registered correctly.
    • A quick inspection of the CustomElementRegistry showed that they were registered
  2. There is some sort of sanization in play that strips out "invalid" elements because of the usage of innerHTML

The sanitation turned out to be the culprit. By using the DomSanitizer we were able to mark our HTML as safe and get our code working as expected.

Code example

/* some-component.component.ts */
import { DomSanitizer } from '@angular/platform-browser';

@Component({
    selector: 'some-component',
    templateUrl: './some-component.component.html',
})
export class SomeComponent {
    constructor(private _sanitizer: DomSanitizer) {}

    templateHtml = this._sanitizer.bypassSecurityTrustHtml(`
        <webcomponent-bank-account>
            <span slot="label">John Doe</span>
            <span slot="balance">€ 100.000</span>
            <p slot="account-number">1234567890</p>
        </webcomponent-bank-account>
    `);
}
Enter fullscreen mode Exit fullscreen mode
<!-- some-component.component.html -->
<template #webComponentTemplate [innerHTML]="templateHtml">
</template>
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
patricksevat
Patrick Sevat

Posted on July 19, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related