Strongly-typed templates
Many users may be familiar with libraries such as FAST Element or lit-html . They want to know how to write strongly-typed templates with Aurelia.
In the following, we will see how to write a template as follows and introduce it to Aurelia.
export const buttonTemplate = html<BootstrapButton>`
<button class="btn btn-primary btn-${x => x.size} ${x => x.block ? 'btn-block' : ''}" ref="bsButtonTemplate">
${(x) => x.getName()}
</button>
`;
To do this, we need two simple pieces of functionality so, create a strongly-typed-template
file.
type TemplateValue<T> = { [P in keyof T]: T[P] extends Function ? never : P }[keyof T] | ((val: T) => unknown);;
function parse(val: any) {
const variable = val?.toString();
const isFunc = variable.indexOf("f") > -1;
if (!variable) return "";
const firstParanthesis = variable.indexOf("(") + 1;
const secondParanthesis = variable.indexOf(")");
const variableName = variable.substring(firstParanthesis, secondParanthesis) || variable[0];
const regex = new RegExp(`${variableName}\\.`, "g");
return variable
.substring(
isFunc ? variable.indexOf("return") + 7 : variable.indexOf("=>") + 3
)
.replace(regex, "");
}
export const html = <TSource = any>(
strings: TemplateStringsArray, // html text
...values: TemplateValue<TSource>[] // variables which they are functions.
) => {
let html = "";
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
const currentString = strings[i];
const value = values[i];
html += currentString;
if (typeof value === "function") {
const parsed = parse(value);
html += `\${${parsed}}`;
continue;
}
html += `\${${value}}`;
}
html += strings[strings.length - 1];
return html;
}
The idea behind the code is really simple. First, we separate strings and variables parts inside html
function.
string(s):
<button class="btn btn-primary btn-lg" ref="atButtonTemplate">
</button>
variable(s):
x => x.size
x => x.block ? 'btn-block' : ''
(x) => x.getName()
Then, for variable parts, we remove the lambda part (VARIABLE => VARIABLE.
) by regex via parse
function. Finally, an HTML is created according to the acceptable standards for the Aurelia template engine.
The generic parameter in this function is actually your view-model.
import { html } from './strongly-typed-template';
// BootstrapButton is my view-model
export const buttonTemplate = html<BootstrapButton>`
<button class="btn btn-primary btn-${x => x.size} ${x => x.block ? 'btn-block' : ''}" ref="bsButtonTemplate">
${(x) => x.getName()}
</button>
`;
Now, we have to introduce this strongly-typed template to Aurelia via template
option.
import { buttonTemplate } from "./bs-button-temlate";
@customElement({ name: "bs-button", template: buttonTemplate /* HERE */ })
export class BootstrapButton /* view-model */ {
@bindable({ mode: BindingMode.toView }) public size: string;
@bindable({ mode: BindingMode.toView }) public block: boolean;
getName() {
return "Primary Button";
}
}
Last updated
Was this helpful?