LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • 0001 to 0023
      • 0088 to 0723
      • 0901 to 0908
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page

Was this helpful?

Export as PDF
  1. Developer Guides
  2. Recipes

Strongly-typed templates

PreviousSignalR integrationNextTailwindCSS integration

Last updated 1 year ago

Was this helpful?

Many users may be familiar with libraries such as or . 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.

strongly-typed-template.ts
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.

bs-button-template.ts
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.

bs-button.ts
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";
    }
}
FAST Element
lit-html