When it comes to progressively enhancing an application we already have enhance API which allows us to progressively enhance a page on first load, but what about enhancing a page with dynamically added elements after everything has loaded? Perhaps your application dynamically inserts HTML into your page.
If you have experience working with AngularJS, you might be familiar with the ability to dynamically compile HTML using $compile
which works for dynamically inserted HTML and other things that occur after the initial bootstrapping phase is done.
Say hello to my little friend… Templating Engine.
Instead of enhancing a page at runtime, we can enhance any part of the page even after Aurelia has completely bootstrapped and loaded.
There are caveats that you need to be aware of when using the enhance method on the Templating Engine class before using it.
You can only enhance DOM elements — As you are probably already aware the enhance method is for enhancing elements. You can’t use it to enhance strings or objects, this is purely a templating method. The element you are enhancing also needs to already be in the page. This won’t allow you to isomorphically enhance a server-side string or element unfortunately.
You cannot enhance an already enhanced element — You need to ensure when you use the enhance method you are specific about the part of your application you want to enhance. You will encounter issues if you try enhancing an already enhanced element or part of your application that Aurelia has already enhanced.
Why would I use this?
Honestly, the decision to use the enhance method shouldn’t be taken lightly. There are plenty of ways the Aurelia framework lets you dynamically compose elements in the DOM like the <compose>
element which let’s you dynamically compose views and view models.
I encountered a situation recently where the server was generating SVG elements and then returning the SVG string with string interpolation and other Javascript specific features right in the string. The SVG was being returned with strings for binding to colour values and text values.
Obviously using the compose functionality was out of the question because I was dealing with strings, I do not actually know how the SVG will be structured before it is returned. I needed a way to “Aureliaise” my SVG elements and associate them with a class view model that define the structure of them.
The only scenario I can think of if this exact situation: You are dynamically inserting HTML into your Aurelia application after the initial render. You want this dynamic content to be useable in Aurelia.
As I mentioned earlier, I am loading massive SVG elements from the server and then associating them with a view model which defines the structure of the SVG. The SVG has some colour values I want to dynamically change.
The View Model
import {inject, TemplatingEngine} from 'aurelia-framework';
class SvgViewModel {
outlineColour = '#FF0000';
fillColour = '#29FBDF';
}
@inject(TemplatingEngine)
export class EnhancedSVG {
templatingEngine = null;
viewModelInstance = null;
svgContents = '';
constructor(templatingEngine) {
this.templatingEngine = templatingEngine;
this.viewModelInstance = new SvgViewModel();
}
attached() {
setTimeout(() => {
this.svgContents = '<svg height="210" width="500"><polygon points="100,10 40,198 190,78 10,78 160,198" css="fill:${fillColour};stroke:${outlineColour};stroke-width:5;fill-rule:nonzero;"/></svg>';
setTimeout(() => {
this.letsEnhance();
}, 1000);
}, 1000);
}
letsEnhance() {
let el = document.querySelector(`svg`);
if (el) {
if (!el.querySelectorAll('.au-target').length) {
this.templatingEngine.enhance({element: el, bindingContext: this.viewModelInstance});
}
}
}
}
The View
<template>
<div id="svg-holder" innerhtml.bind="svgContents"></div>
</template>
An explanation
What did you just see? You saw what the enhance API functionality when used in a bootstrapping initial load capacity uses. We are actually hooking into the enhance method, but doing so after the application has loaded.
This means you can have the server render massive strings of HTML with bindings, events and string interpolation. While it won’t let you achieve an isomorphic workflow of rendering Aurelia apps on the server side and returning the resulting HTML, it gives you this flexible enhance functionality.
The magic that occurs in the above example is in the letsEnhance()
method. You can see we are looking for our newly added element in the page. In our situation we are looking for an SVG.
We then check if the element exists, if it does we can proceed to check that nothing inside of our SVG has a class of au-target
– this class gets added to various parts of the DOM by Aurelia itself. If you look in the page of your application, you will notice various DOM elements have this class. This essentially earmarks it as belonging to a particular part in Aurelia.
We do this check because if we were looking for multiple elements to enhance, we don’t want to enhance elements that have already been enhanced or it will break them.
In our above example the class check is probably redundant, but I always use it just to make sure. There is also the possibility some situations might require a check for the parent element and the au-class
as well.
Then we have the actual logic component using the enhance method on the templateEngine class. The first argument is the DOM element we want to enhance. It needs to have a parent element or it won’t work. So our SVG actually has a wrapper DIV to meet this check. The second argument is a bindingContext which is a view model.
This view model is just like any other view model you might have in your Aurelia application. It can have methods, variable values (as seen above) and you can use delegates, trigger, string interpolation and other things you can do normally in your Aurelia app.
Conclusion
As you can see, there is some great underlying power in Aurelia. I have yet to run into any limitations using the enhance at will method, that is not to say there isn’t any. I just have not encountered any.
I really just wanted to share this cool functionality in Aurelia even if it is of no use to anyone else. The fact you can do things like this is one of the reasons I love this framework, powerful and flexible if you take the time to truly understand its internals.
As always, if you have any questions, hit me up in the comments section. I will gladly help explore other scenarios where enhance might be helpful for your use-case.
Have you gotten enhance to work with .bind, .trigger or .delegate? Simple string interpolation works fine for me but if I try to set up binding on an input or delegating an event to a vm method, nothing happens
Found a solution. I was trying to enhance an single element, but only simple interpolation was working. Once I surrounded it with a span and enhanced the span, binding and event delegation kicked in.
This is perfect – I was looking for this functionality. Thanks for sharing 🙂
Very interesting. How do you unbind/undo what templatingEngine.enhance did? That way you could call this multiple with different values for svgContents
Are there any concerns with removing the elements from the DOM after they have been enhanced? Do we need to call a garbage collection method or something to ensure there are no memory leaks?