How to use Web Components in HTMLTemplateElement in Angular
Tags: webcomponents, angular, htmltemplateelement
Published: 2020-07-19 22:14:56

**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](#code-example)

-----------

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`](https://developer.mozilla.org/en-US/docs/Web/API/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](https://developer.mozilla.org/en-US/docs/Web/API/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](https://stackoverflow.com/questions/51925660/angular-6-create-htmlelement-from-htmltemplateelement), 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`](https://angular.io/api/platform-browser/DomSanitizer) we were able to mark our HTML as safe and get our code working as expected.

### Code example
```typescript
/* 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>
    `);
}
```

```html
<!-- some-component.component.html -->
<template #webComponentTemplate [innerHTML]="templateHtml">
</template>
```