Become An Unstoppable Templating Wizard With Aurelia 2's Template Compiler

Aurelia 2, the latest incarnation of the Aurelia framework, is packed with improvements and new features. Among these, the revamped template compiler stands out for its potential to significantly boost your productivity. This article takes a deep dive into the template compiler, focusing on functionality that allows developers to intercept and modify the compilation process of an application’s templates.

Introduction to the Template Compiler

Aurelia’s template compiler operates behind the scenes, processing templates and providing hooks and APIs that allow developers to modify their default behaviour. One critical use case for the template compiler is the ability to preprocess a template before it’s compiled. This could be for reasons such as accessibility validation, the addition of debugging attributes, or even the injection of custom attributes or elements.

Harnessing the Power of Template Compiler Hooks

Currently, the primary hook provided by the template compiler is compiling. This hook gets invoked before the template compiler starts compiling a template, allowing you to modify it before its compilation.

Hooks can be declared globally (at startup) or within a local container (at runtime or via convention-based imports).

Let’s take a look at an example of declaring a global hook using the decorator approach:

import Aurelia, { templateCompilerHooks } from 'aurelia';

@templateCompilerHooks
class GlobalHook {
  compiling(template: HTMLElement) {
    template.querySelector('table').setAttribute(someAttribute, someValue);
  }
}

Aurelia.register(GlobalHook);

In this example, the compiling hook modifies a table element within the template. The querySelector method targets the table element, and then the setAttribute method adds or modifies an attribute.

You would globally register this inside of main.ts

Practical Examples of Template Compiler Hooks

Now that we’ve covered the basics let’s delve into more advanced practical examples of using template compiler hooks.

Enforcing Image Accessibility

A compelling use case for the compiling hook is enforcing the inclusion of alt tags for all img elements, enhancing accessibility.

Here’s how to do it:

import Aurelia, { templateCompilerHooks } from 'aurelia';

@templateCompilerHooks
class ImageAltHook {
  compiling(template: HTMLElement) {
    const images = template.querySelectorAll('img');
    images.forEach(image => {
      if (!image.hasAttribute('alt')) {
        image.setAttribute('alt', 'placeholder alt text');
      }
    });
  }
}

Aurelia.register(ImageAltHook);

In this code, we’re selecting all img elements within the template. For each image, we check if an alt attribute exists. If it doesn’t, we add one with placeholder text. You can even add inline styles to flag components that must be addressed.

Injecting Custom Attributes

Another advanced use case is injecting custom attributes into specific elements. For instance, you may want to add a my-custom-attr attribute to all div elements:

import Aurelia, { templateCompilerHooks } from 'aurelia';

@templateCompilerHooks
class CustomAttrHook {
  compiling(template: HTMLElement) {
    const divs = template.querySelectorAll('div');
    divs.forEach(div => {
      div.setAttribute('my-custom-attr', 'custom value');
    });
  }
}

Aurelia.register(CustomAttrHook);

Here, we’re selecting all div elements and adding a my-custom-attr attribute with a value of custom value.

Injecting as-element Directives

You can also use the compiling hook to inject as-element directives into your template. For example, if you want to treat a specific div as a custom element, you could do the following:

import Aurelia, { templateCompilerHooks } from 'aurelia';

@templateCompilerHooks
class AsElementHook {
  compiling(template: HTMLElement) {
    const targetDiv = template.querySelector('div#target');
    if (targetDiv) {
      targetDiv.setAttribute('as-element', 'my-custom-element');
    }
  }
}

Aurelia.register(AsElementHook);

In this example, we’re selecting a div with the id target and adding the as-element attribute to it, effectively treating it as my-custom-element. In a real-world example, you might want to replace buttons with custom elements or add attributes to add behaviours to certain elements.

Conclusion

With its powerful hooks, Aurelia 2’s template compiler opens up possibilities for preprocessing and adjusting templates before compilation. This article hopefully provides you with a deeper understanding and practical examples of using this feature. As the Aurelia community continues to explore and innovate, we can expect to see even more creative uses for template compiler hooks in the future.