How to use Web Components in HTMLTemplateElement in Angular?

TLDR

  1. Use [innerHTML] on the code-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 component, also known as HTMLTemplateElement.

The cool thing about the tag is that its contents are not actually rendered by the browser as long as they reside within . 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 tag to the DOM using Angular, it started double rendering! ?

When inspecting the element we also noticed that in Angular it did not yield a new Document fragment as it does in plain HTML…

This means the 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 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>
    `);
}
<!-- some-component.component.html -->
<template #webComponentTemplate [innerHTML]="templateHtml">
</template>

About the author

Patrick Sevat
Senior Frontend Developer

Patrick is crazy about JS/TS and has the small ambition to transform Rabobank from a bank with an IT department to a tech company with a banking license.

Related articles

Tips for writing Page Objects and Component Objects

  • 16 September 2021
  • 1 min
By Patrick Sevat

Tackling asynchronous integration testing in Senses

  • 16 September 2021
  • 3 min.
By Patrick Sevat