A possible solution to this may be to accept a template string and the component would need to know how to deal with it.
A function for handling template strings:
export const template = (str, vars): string => new Function(`return \`${ str.replace(/\${/g, '${this.') }\`;`).call(vars);
Essentially builds a function on the fly to treat a given string as if it were a `template ${string}`
Usage:
let myTemplate = 'Just a ${field}';
let vars = { field: 'test' };
let str = template(myTemplate, vars); // "Just a test"
Option 1:
The Angular component could accept a template string and perform this template against it, then feed it to the DOM sanitizer for safe HTML to be used as an [innerHTML]
Annoying, but an option.
Option 2:
Could accept it as content
<my-map [markers]="markers">
<template>
<h1>${marker.title}</h1>
<div>${marker.subtitle}</div>
</template>
</my-map>
Angular Elements doesn't support ngContent
or ngContentChildren
(doesn't convert them to <slot>
or anything either), so this option would require fancy footwork on behalf of the Angular Component.
The component's template needs to spit out the given content, something like
<ng-content></ng-content>
Somewhere in it's template so it'll render the given content.
Then in the component, grab the DOM contents to be templated. (probably after ngOnInit
)
this.hackyTemplateHtml = this.elRef.nativeElement.querySelector('template').innerHtml;
If requiring multiple templates, could have the implementation send them with a unique attribute or class <template class='marker-template'>
For the template loop, would need to send each marker to be templated:
<div *ngFor="let marker of markers; trackBy: trackByTitle">
<ng-container *ngTemplateOutlet="hackyTemplateHtml ? hackyTemplate : defaultTemplate;
context:{$implicit:marker}" >
</ng-container>
</div>
<ng-template #defaultTemplate let-marker>
<h1>{{marker.title}}</h1>
<div>{{marker.subtitle}}</div>
</ng-template>
<ng-template #hackyTemplate let-marker>
<div [outerHTML]="templateHacky(marker)"></div>
</ng-template>
The ngFor
should probably implement a trackBy
so it's not re-templating constantly. and the [outerHTML]
is so it replaces the <div>
, so it's not wrappered.
Back in the component:
trackByTitle(idx, marker) {
return marker.title;
}
templateHacky(marker: Marker) {
let templated = template(this.hackyTemplateHtml, { marker });
return this.sanitizer.bypassSecurityTrustHtml(templated);
}
Also definitely hacky. Best I could think of as plain HTML/JS implementation.
I'm working on a solution for react/vue to be able to send the equivalent of an Angular TemplateRef
to Angular Elements to be treated as a normal template. Needs further thinking of how to do it frameworkless though.