TLDR
- Use
[innerHTML]
on the<template>
tag - Use
DomSanitizer.bypassSecurityTrustHtml()
to make sure your Custom Elements are not stripped from your provided HTML string
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:
- The Web Components were not registered correctly.
- A quick inspection of the
CustomElementRegistry
showed that they were registered
- A quick inspection of the
- 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>
Top comments (4)
Googling this issue was impossible as noted. I was pointed to this post by a StackOverflow user.
Any idea or reference in the official docs why Angular is handling <template> differently?
Hi @ppbitb ,
I have not dived into this further by going into the Angular source code and I haven't found any reference to this behavior in the documentation (so far). If you do come across any information please let me know and I'll update the article!
Thank you for your response.
I haven't found anything definitive, just sharing half-baked thoughts:
Thank you for sharing this nice article Patrick!