Menu

A Conventional Blog for Convention Based Coders

The Aurelia-webpack libarary is pretty awesome. It makes excellent use of Promises to handle pop-ups, and while it has a few shortcomings it ticks 95% of the boxes with very little overheads and no external dependencies.

But… getting it working with webpack is another matter altogether!

Because aurelia-dialog has embedded css, the normal loading methods in the webpack loader struggle, and they either embed things where they shouldn’t or apply the wrong loader. In addition to this, the module loader fails to find viewmodels if you use the class instance resolution through the DI container, so you have to work around that too.

One problem with dirty checking is that unless you actually check for a changed value you will fire too many updates on an underlying bound control.

Sometimes this doesn’t matter, but if you’ve got something bound to a repeat.for, then it certainly does!

So here’s an extension to the previous & dirty binding behavior that ensures the change is only bound (and corresponding method in your VM is only called) if the value actually changes, and adds a nifty new callback feature to detect the changes in our VM:

However, unlike the standard binding techniques, this binding will trigger a firstnameChanged and ‘getLastnameChanged` events, even if those properties are nested inside another object, or if they are methods!

You can also group your changes by specifying the event to be called in your VM:

Writing debug output while developing is great. After a few days you have pages and pages of logs in your browser console, and when it comes time to run tests or deploy to UAT you can trawl through thousands of lines of code and comment them all out!

Or… you can use the built in Aurelia logger! This lets turn on or off error output, and even choose the level so you can show only serious errors at deployment time. You can even add runtime options to switch the error level so you can get more detailed output on your live system.

So how do we make this as easy to use as the console? Simple, we simply create a module exposing the Aurelia logger like this:

log.js

log.js

JavaScript

1

2

import{LogManager}from"aurelia-framework";

export varlog=LogManager.getLogger('insight');

Then in out code we say something like

JavaScript

1

import{log}from"../services/log";

Then, whenever we want to simply say

JavaScript

1

2

3

log.debug('Toaster loaded: ',this.toasterService);

log.info('Yes, I made it this far!');

log.error('Serious problem loading waffles');

Now, in our startup code, instead of the usual .developmentLogging() command, we can add something with a little more detail as to what we want to display, such as

JavaScript

1

2

3

4

5

import{LogManager}from"aurelia-framework";

import{ConsoleAppender}from"aurelia-logging-console";

LogManager.addAppender(newConsoleAppender());

LogManager.setLevel(LogManager.logLevel.debug);

This tells Aurelia that by default we want our logs to go to the console, although we could also direct them to a remote logging service, and also sets the level at which we’d like to display them.

We can also now change the log level based on the URL:

JavaScript

1

2

3

LogManager.setLevel(window.location.search.match(/.*\?debug.*/i)

?LogManager.logLevel.debug

:LogManager.logLevel.error);

Alternatively we could look for localhost in the host name to always enable debug logging on local test machines.

Note the use of the name ‘insight’ in the getLogger method. You can add your own application name there, and it will appear in the console log like this:

But what happens if the users first or last name changes dynamically? Oh dear, functions are only evaluated once at bind time…

Hang on! Let’s make it a property accessor! They are by default dirty-checked!

JavaScript

1

2

3

get userFullName(){

returnthis.firstName+' '+this.lastName;

}

Easy! We just say <div>${userFullName}</div>!

OK… so now it’s updated every 120ms or so, and we can optimise it by adding @computedFrom() in front:

JavaScript

1

2

3

4

@computedFrom('firstName','lastName')

get userFullName(){

returnthis.firstName+' '+this.lastName;

}

So now it will only update when one of those dependent variables changes. Awesome.

Now, I use full-name in several places on-screen, so I want the full name displayed with a variable delimiter, so let’s add a parameter to the call of ‘delim’.

Uh-oh, accessors don’t allow parameters, so it’s back to a method. But methods don’t allow dirty checking or @computedFrom… or @observable…

There are two suggested options; Signals or dummy dependency parameters:

Signals involve adding a ‘signal’ call whenever you want something rebound, and as a messaging system they can be acknowledged by many fields. In this case we can say

XHTML

1

<div>${userFullName(' ') & signal:'my-signal'}</div>

then in our code we might create lastNameChanged and firstNameChanged methods that call the signal method on an injected BindingSignaller:

JavaScript

1

signaler.signal('my-signal');

I’m not wild on this, as I don’t like arbitrary string event names, I don’t want to write interceptors for every property that might ever need a signal.

An alternative is to add dummy properties in the binding so that Aurelia automatically rebinds when one of them changes (thanks to some of @danyow’s awesome magic dependency detection).

JavaScript

1

2

3

userFullName(delimiter,...params){

returnthis.firstName+' '+this.lastName;

}

The syntax ...params is called a ‘spread’ operator and means ‘as many things as you like here’ (rather like the old **args in C coded apps)

Now in our view we can say <div>${userFullName(' ', firstName, lastName)}</div> and our function will only be called when a dependent property updates! Awesome…ish. Now we have exposed the underlying implementation of our model into our view, and what if our method has a dependency on the time, or on 5 different private variables?

A final option is to do some sort of periodic update. We can use signals in a time:

After writing a relatively simple application I discovered that the bundle size for my application had grown to 10.4MB!

Well, after a bit of diving into the un-minimized bundle I realised that when I included brace (a browserify compatible version of ace editor) it was helpfully including the syntax and support code for every syntax.

This isn’t webpack’s fault, it’s because somewhere in the solution there’s an expression such as require('mode/'+lang), so webpack does some smart regex on it and bundles the entire mode/ folder so it can always resolve at runtime, no matter what the variable lang is set to.

However, I know that I only use a handful of those, so how do we tell webpack to ignore the others?

It’s quite simple. There’s a plugin installed by default in webpack called ContextReplacementPlugin. This allows you to define a regex expression for the folder you want to filter items from, then specify the files to include in that folder.

Unfortunately things are not quite this simple. When the browser loads in the template it very helpfully validates the structure of the HTML, notices that you have an invalid tag inside your table definition, and very unhelpfully removes it for you before Aurelia even gets a look-in.

So what is we add containerless to our compose statement? That will remove the ‘compose’ tags! Well, still no good, as the browser has already removed the inner tag.

So, what we want is a way of saying “take this tag, which is valid in this location, but make it act as though it’s a completely different tag”

That’s where the attribute as-element comes in. We can give Aurelia a <tr> tag but tell it to treat its contents as though it were a compose tag!

While we’re used to simply sticking a <script> tags inside templates, this doesn’t work in Aurelia because the browser doesn’t treat injected DOM elements in the same way as traditional DOM manipulation.

So what happens when we want to do something like inject Google Ads or some sort of dynamically generated script from a remote location?

The code below is designed to address this. It takes a URL as a parameter, then imply injects a script element to the body of your page, but maintains a reference so the block can be removed when the current tag is removed.

To avoid adding <require from='../components/mything'></require> to every view I use those components in, I create a file called globals.ts

JavaScript

1

2

3

4

5

6

7

8

9

10

export functionconfigure(config){

config.globalResources([

'component/date-format',

'component/sort',

'component/filter',

'component/debug',

'component/datepicker',

'component/jsonEditor'

]);

}

and I add this to the main bootstrapper in my main.ts like this:

JavaScript

1

.plugin('component/globals');// install our app's resources

Being Aurelia, there is nothing magical about the concept of a plugin, and behind the scenes it simply calls the configure() method during initialisation. This makes it a convenient place to do some initialisation and avoids polluting my main start-up classes with references to other classes.

(Note that as with many jQuery plugins in JSPM you have to use jspm install npm:jquery-datetimepicker -o "{format: 'global'}")

Adding it to Aurelia proved a little tricky though. First I thought I’d quickly ‘bodge’ it in to check the functionality, so I added some code to the attached() method of my view to call the jquery extension it had added:

XHTML

1

2

3

4

5

6

<input

value.bind="options.deactivateDate"

placeholder="Deactivate at..."

type="datetime"

ref="dt_activate"

/>

JavaScript

1

$(this.dt_activate).datetimepicker();

But things didn’t quite go as expected. While the popup appeared, and the value changed, it wasn’t being bound to the underlying deactivateDate in my object.

Well, it turns out that this is an issue in that jQuery doesn’t always fire change and input events when modifying values in input fields, so the easiest way to solve this is simply to call the event yourself.

So, I might as well bundle this whole thing into a custom attribute, saving the hassle of adding anything in attached() as this will be resolved automatically at the appropriate time:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

import{inject,customAttribute}from'aurelia-framework';

@customAttribute('datepicker')

@inject(Element)

exportclassDatePicker{

constructor(privateelement){

}

attached(){

($(this.element)asany).datetimepicker()

.on('change',e=>fireEvent(e.target,'input'));

}

detached(){

// remove it in here if possible!

}

}

functionfireEvent(element,name){

varevent=document.createEvent('Event');

event.initEvent(name,true,true);

element.dispatchEvent(event);

}

I save this as datepicker.tsand add it to the list of global resources (so I don’t have to bother using <require> to import it), and my final front-end code looks like this:

XHTML

1

2

3

4

5

6

<input

value.bind="options.deactivateDate"

placeholder="Deactivate at..."

type="datetime"

datepicker

/>

The only other thing to do is to tidy the display up a bit, so it’s not displaying the date and time in full UTC format.

Simply done using a ValueConverter (using moment imported from NPM):

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

import*asmoment from'moment'

exportclassDateFormatValueConverter{

toView(value){

console.warn('to',value);

returnmoment(value).format('YYYY-MM-DD HH:mm');

}

fromView(value){

console.warn('from',value);

returnmoment(value,'YYYY-MM-DD HH:mm');

}

}

Bear in mind that this is being displayed in current time, not UTC, so you may choose to modify the converter to display locally but read and write as UTC to the underlying object.