How To Build Complex, Large-scale Aurelia Apps With Aurelia Store

If you’re new to state management or you’re familiar with it and not using Aurelia Store already (you should), today we are going to be looking at how you can integrate Aurelia Store into your Aurelia applications and make the life of your development team and yourself a lot less stressful.

A lot of state management articles will wheel out the TV and VCR player on the tray table with wheels and default to the cliche shopping cart or todo application example to show you how to work with state management.

Look, they’re great simple examples showing you how to work with state management, but they are quite far removed from a large-scale application which could be comprised of hundreds of actions and troves of data going to and from the server.

Unless you’re building a todo application or basic shopping cart, these examples are not going to answer all of your questions nor reflect a real application with a real team working on it.

The truth of the matter is, real-world applications (especially for large products) can get big. I worked on an Aurelia application that had upwards of 40 components, not-to-mention value converters, attribute. a stack load of modals, routed sections and an impressive amount of singleton classes.

What problems does state management solve, and do I even need it?

State management isn’t something you always need, but if you’re working with a growing collection of data (from an API or user-entered) and you’re sharing it across your application, state management is unavoidable or you’ll end up with singleton soup.

For a long time using Aurelia, I actually avoided using state management, even though other solutions predate Aurelia Store such as MobX and Redux. The problem that most other state management solutions share is they require serious “buy in” to use them, they have a cost.

Fortunately, Aurelia Store aligns well with the Aurelia mantra of “just write code” so it doesn’t strictly enforce a specific way of working other than the basics like not directly mutating state and registering actions.

The biggest problem state management addresses in Javascript is: assign by reference. Every variable and value you reference is passed by reference, meaning it can be overwritten or accessed from anywhere in your application without any kind of visible trail which can be disastrous.

When you opt-in to state management, you’re explicitly choosing to update and read data from a centralised store via an abstraction. Good state management prevents you from accidentally overriding a value in the store without leaving a trail (still possible, but more difficult).

One example of where state management shines is Aurelia’s binding system. Picture this scenario, you have a form with 4 inputs and those inputs are using value.bind referencing properties on some object or class. When those values update, the object values also update, form input value bindings are two-way by default.

Now, imagine that object is being shared across your app and you have an array of users. The form allows you to update a user and everywhere sees the change immediately (pass by reference). Now imagine an invalid value is entered or something breaks, how do you revert it? You can’t (unless you have a copy of the object).

If you want to know when the value was updated, how do you do that? In Aurelia, you’ll need to use the propertyObserver API or simplify your app and use @observable to observe changes to values and act accordingly. It results in a lot of additional and potentially performance draining code that becomes a maintenance nightmare over time.

Aurelia Store features

  • A reactive subscription observation system, so when changes are made to the store you can subscribe to the state itself and get the updated changes anywhere in your application. Think when a user logs in and out when a collection of products is loaded.
  • State is not directly mutated, actions are used to update the state and leave a history you can inspect using Redux Dev Tools as well as undo or replay.
  • Supports middleware. Allows you to integrate code that runs at various points of the state lifecycle, allowing you to filter values or check permissions.
  • Aurelia Store gives you a clearer view of your data structures and separates the data layer from the view and view-models (easier to test, easier to refactor).
  • Allows you to have safe and fast cross-component communication, meaning you no longer need to abuse the Event Aggregator or Router to pass data around.
  • Easy to debug using the Redux Dev Tools which the plugin completely supports. A browser plugin for visually seeing actions and state as it happens, as well as call actions.
  • Supports caching out-of-the-box, allows you to cache your data in localStorage and create offline/poor internet connection capable applications.

Structuring an Aurelia app for Aurelia Store

Unlike existing state management solutions, Aurelia Store doesn’t require you to structure your application in a specific way. This is both good and bad because it means you can choose a structure and go down a particular path only to find you chose the wrong architect.

Below is the approach I take in my Aurelia applications using a store, I will create a folder called store in my src directory.

Please keep in mind that I use TypeScript, so the below examples and explanations will be written in TypeScript and vanilla Javascript differs slightly.

src
├── app.html
├── app.ts
├── ...
├── store
│   ├── actions
│   │   ├── auth.ts
│   │   ├── user.ts
│   ├── store.ts
│   ├── state.ts
└── ...

The actions folder contains files which contain functions (actions) that are exported and can be imported where needed. The auth.ts example might have a login and logout method setting and unsetting the currently authentication user.

The user.ts might have actions related to the user like getting content specific to their account, getting an avatar or updating their profile.

The store.ts file is interesting because I actually import the Store itself and get a reference to it via the Aurelia Dependency Injection container. Here is what the inside of my store.ts in new Aurelia applications I build looks like these days:

import { Container } from 'aurelia-framework';
import { Store } from 'aurelia-store';
import { State } from './state';

const store: Store<State> = Container.instance.get(Store);

export default store;

The reason I do this is so I do not have to follow the official examples where the Store is injected into every view-model you want to register actions from within. It was tedious and I wanted an easier way. It also allows me to actually register actions from within the action files themselves instead of view-models, which makes more sense to me.

If you want to manually inject the store into your view-models when needed, the official documentation and examples encourage this approach. This is just another option.

The state.ts file has an object which is the application state structure itself. A basic application using the above example of a structure might look like this:

export interface State {
    auth: {
        loggedIn: boolean;
        token: string;
        refreshToken: string;
    };

    user: {
        id: string;
        username: string;
        avatar: string;
        profileFields: any;
    };
}

export const initialState: State = {
    auth: {
        loggedIn: false,
        token: '',
        refreshToken: ''
    },
    user: {
        id: '',
        username: '',
        avatar: '',
        profileFields: {}
    }
};

Configuring the plugin

We now have the state configured, so you’ll want to pass it as an argument to the aurelia-store plugin registration inside of your main.ts file inside of the exported configure method.

import { Aurelia } from 'aurelia-framework'
import { initialState } from './store/state'; // exported initialState object

export function configure(aurelia: Aurelia) {
  aurelia.use
    .standardConfiguration()
    .feature('resources');

  ...

  aurelia.use.plugin('aurelia-store', { initialState });

  aurelia.start().then(() => aurelia.setRoot());
}

And now we get into the actions themselves…

We glimpsed over actions earlier because to write them you really need your state in place first. Actions are what mutate the state values and you can actually do quite a lot with them. On a basic level, the first argument is the state object itself and you can pass parameters (which are subsequent arguments).

Actions can be sync or async. This means you can do things like make API requests from within actions and make your application asynchronously call and wait for the returned promise to resolve or reject. Let’s look at our fictitious auth.ts actions file.

import store from 'store/store';
import { State } from 'store/state';

export function login(state: State, token: string, refreshToken: string) {
    const newState = Object.assign({}, state);

    newState.auth.loggedIn = true;
    newState.auth.token = token;
    newState.auth.refreshToken = refreshToken;

    return newState;
}

export function logout(state: State) {
    const newState = Object.assign({}, state);

    newState.auth.loggedIn = false;
    newState.auth.token = '';
    newState.auth.refreshToken = '';

    return newState;
}

store.registerAction('login', login);
store.registerAction('logout', logout);

If you have already read the Aurelia Store documentation (and you should), then you would know that actions should NEVER mutate the passed in state property directly and should always be shallow cloned using something like Object.assign or the spread operator { ...state } to make a copy of it.

You will also notice our login action takes the state like all actions do, but has two other arguments for the token and refreshToken. When the action is dispatched, these can be passed which I’ll show you next.

Actions should always return a copy of the state, which then does the work of update the state object itself. Never just assigning new properties to state, because otherwise, you encounter the pass by reference issue we talked about earlier which is bad as there will be no trail of your updates (hard for testing and debugging).

Similarly, the users.ts actions file might have an async function in it…

import store from 'store/store';
import { State } from 'store/state';

// A fictitious API service you might have
import { Api } from 'services/api';

// Use Aurelia DI to get the api because we are not inside of a view-model
const api: Api = Container.instance.get(Api);

export async function getAvatar(state: State) {
    const newState = Object.assign({}, state);

    try {
        // An API function that accepts the current user ID
        // notice we reference the user object to get the ID?
        const avatarImage = await api.fetchUserAvatar(newState.user.id);

        // Store the avatar
        newState.user.avatar = avatarImage;
    } catch (e) {
        console.error(e);
    }

    return newState;
}

store.registerAction('getAvatar', getAvatar);

Triggering actions

We now have our store setup and configured. Now picture you have a login page with basic username and email fields, you hit a login button and it goes off to the server.

import { autoinject } from 'aurelia-dependency-injection';
import { dispatchify, Store } from 'aurelia-store';

import { State } from 'store/state';

import { login } from 'store/actions/auth';

@autoinject()
export class Login {
    public state: State;
    private subscription: Subscription;

    constructor(private store: Store<State>) { }

    bind() {
        this.subscription = this.store.state.subscribe((state) => this.state = state);
    }

    unbind() {
        this.subscription.unsubscribe();
    }

    doLogin() {
        api.login(username, password)
            .then(result => result.json())
            .then(auth => {
                dispatchify(login)(auth.token, auth.refreshToken);
            });
    }
}

I am using the dispatchify method so I don’t have to manually inject the store into the view-model itself and dispatch the function that way. Worth acknowledging is when you dispatch a function, you pass the function itself and not the name.

The dispatchify method returns a function which is the action itself, which allows you to pass values to its arguments. In our case, we are mocking a login method that calls an API and then if successful, will store the token and refreshToken values.

Async/await actions

We showcased a pretty basic example of dispatching the login action to set the token and refreshToken in the state. Now, let’s showcase how powerful asynchronous actions are, by calling our get user avatar action.

I have been asked this on a few occasions by people interested in the Aurelia Store plugin: is it okay to make API requests and request data from within actions? Absolutely. The fact actions support returning promises makes them the ideal candidate for encapsulating everything related to your data.

You will notice the code looks very much the same as the example above.

import { autoinject } from 'aurelia-dependency-injection';
import { dispatchify, Store } from 'aurelia-store';

import { State } from 'store/state';

import { login } from 'store/actions/user';

@autoinject()
export class User {
    public state: State;
    private subscription: Subscription;

    constructor(private store: Store<State>) { }

    bind() {
        this.subscription = this.store.state.subscribe((state) => this.state = state);
    }

    unbind() {
        this.subscription.unsubscribe();
    }

    async activate() {
        await dispatchify(getAvatar)();

        // If successful, the avatar will be available on this.state.user.avatar
        // which can then be referenced in the view
    }
}

Using getters and computedFrom for sanity

Eventually, you will realise that in your views your referenced state objects and values can be pretty daunting to look at as well as type. You can use computed getters to create shortcuts to values in your store.

Using the above login example, we’ll create a getter to simplify accessing the auth property in our state.

import { autoinject } from 'aurelia-dependency-injection';
import { computedFrom } from 'aurelia-framework'; 
import { dispatchify, Store } from 'aurelia-store';

import { State } from 'store/state';

import { login } from 'store/actions/auth';

@autoinject()
export class Login {
    public state: State;
    private subscription: Subscription;

    constructor(private store: Store<State>) { }

    bind() {
        this.subscription = this.store.state.subscribe((state) => this.state = state);
    }

    unbind() {
        this.subscription.unsubscribe();
    }

        @computedFrom('state.auth') {
            // Allows you to use auth.loggedIn and so on in your views
            return this.state.auth;
        }

    doLogin() {
        api.login(username, password)
            .then(result => result.json())
            .then(auth => {
                dispatchify(login)(auth.token, auth.refreshToken);
            });
    }
}

My approach differs from the official documentation

It is worth pointing out that my approach differs greatly from the official Aurelia Store documentation and examples in the GitHub repository. I am not saying that this is the way you should do it, I am just showing you how I do it and what works for me.

The approach you see above is the same approach I take for all new Aurelia projects using the store. It allows me to neatly organise my actions into bite-sized modules and it just feels very clean.

This is only the start

We didn’t get too in-depth into how you can use the Aurelia Store plugin, because the official documentation does a great job of that already, which you can read here and if you haven’t, I highly recommend you should.

In a separate future article, I will show you how to use Aurelia Store with server-side rendering and implement features such as default state and offline capability.

Aurelia Tips & Tricks

If you are new to Aurelia or perhaps already building something with it, here are some tips and tricks I have learned over the last two years which have helped me write well-performing Aurelia applications.

Keep in mind that Aurelia is like any other framework or library and that it is very easy to write poor performing applications if you’re not careful. Never assume that a framework will stop you from writing bad code.

If vs Show Bindings

In Aurelia there are two attributes: if and show which are similar to one another, but have some fundamental differences. The if binding will add or remove elements from the DOM, whereas the show binding just applies a conditional display:none to whatever element the attribute is used on.

Understandably, for some it is confusing. Do you use if or do you use show? My rule of thumb is defined by how often I am going to show or hide something in my application.

Am I going to show or hide an element in the page regularly or am I conditionally going to show or hide something sporadically or only just once?

The upside of the if binding is when the bound value is false, the elements in the DOM will be removed (freeing up DOM space and bindings will be removed). The downside being Aurelia needs to set up a new controller instance and bindings (which isn’t that big of a deal).

I am a huge fan of a light DOM, so I use if.bind extensively because a large DOM is a lot slower than the time it takes for Aurelia to setup controller instances and bindings. Both have a purpose, it is important to use what works for you.

I do find if.bind works well for me in most cases, no point having elements around if they’re not being used (in my opinion).

Avoid dirty-checking at all costs

If you’re not sure what dirty-checking is, essentially it’s a timer with a 120-millisecond timeout value that repeatedly loops and checks for changes. You definitely do not want dirty-checking in your application (unless you absolutely need it for some edge case).

If you define a getter in your view-model which accesses one or more values in your class and you do not supply the computedFrom decorator, it will be dirty-checked.

An example of dirty-checking can be seen below:

export class MyClass {
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }
}

<template>
    <h1>${fullName}</h1>
</template>

Internally, Aurelia is going to dirty-check this using a setInterval to constantly poll if the value changes, this is bad. Fortunately, using computedFrom we can make this an optimised getter which only reacts when one or more dependencies change.

The below example does not use a timer to poll for changes, it makes the getter reactive.

import {computedFrom} from 'aurelia-framework';

export class MyClass {
    @computedFrom('firstName', 'lastName')
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }
}

<template>
    <h1>${fullName}</h1>
</template>

A note on computedFrom: the this part is implied, so when supply one or more computed values to computedFrom you are automatically referencing the current class and do not need to supply this.

A note on computed arrays

the computedFrom decorator is great for telling Aurelia about your getter dependencies, but it doesn’t work for all values. One such situation is arrays, an array cannot be directly supplied as a getter computed value because it won’t update.

If you’re looking to force a getter to update when an array changes, computed based on its length. When the size of the array changes (items are added or removed) the getter will fire.

import {computedFrom} from 'aurelia-framework';

export class MyClass {
    @computedFrom('arr.length')
    get firstValue() {
        return `${this.arr[0]}`;
    }
}

<template>
    <h1>${firstValue}</h1>
</template>

Think big by thinking small

If you come from an MVC background, then you’ve heard the expression, “fat model, skinny controller” and other countless expressions around MVC architecture which have sort-of been carried over into the front-end development jungle.

I prefer fat nothing in my Aurelia applications (or web applications full-stop). If your view-models and/or views are massive, then you have a code architecture problem which is easily solved by reusable components.

A large view-model or view is eventually going to be your undoing (besides the annoying Git conflicts you will encounter working in a team on large files). Large files which do too much are counterproductive and unnecessarily complicated.

If you want to ensure your application is easy to build upon and make it easier for the browser, build tools and test runners to work with your code: break things up into reusable components.

I highly recommend approaching the architecture of your Aurelia applications using the Single responsibility principle.

Use view-cache

This isn’t documented very well, but Aurelia provides a view-cache which can allow you to cache heavy views. This means if you have quite a complex view and you navigate away, but come back to it later Aurelia will use the view-cache and update all of the appropriate bindings.

The benefits are obvious: the whole view doesn’t need to be recreated when you navigate back to it. Just throw view-cache onto the main opening <template view-cache="*"> tag, which also accepts a cache size value.

If you supply an asterisk * to view-cache it will cache all instances or if you supply a numeric value, cache the number supplied.

Use compose when nothing else fits

Aurelia provides a <compose> custom element which allows you to dynamically compose from within your application. You can dynamically compose view-models, views and pass in data as well.

As great as <compose> is, I try and avoid it wherever I possibly can. It’s not that I think <compose> is bad, it’s the fact that <compose> is an abstraction and where possibly, I try and avoid abstractions in my applications.

I have experienced instances where <compose> has made debugging more difficult than straight-up view-models and components.

Assess your needs and if <compose> is the only option, then by all means use it.

Use Binding Behaviors

Out-of-the-box, Aurelia ships with some great binding behaviours which allow you to make bindings in your applications more efficient and performant.

A binding behaviour in your view templates is denoted by an ampersand “&” and much like value converters, they can be chained and have parameters which accept values.

throttle

A rate-limiting throttling behaviour which allows you to throttle the rate in which the view-model is notified of a change in a two-way binding situation. By default the throttle value is 200ms.

debounce

Very similar to the throttle binding behaviour, the debounce behaviour will prevent a binding from being updated until the debounce interval completes after no changes have been made.

updateTrigger

Allows you to specify which input events signal to Aurelia’s binding system that a change has been made. By default, the events that trigger a change are change and input.

signal

Allows you to tell Aurelia’s binding system to refresh a binding. It works like an event listener on a binding and when you fire an update signal, it refreshes the binding which works similarly to how a first-page load does.

oneTime

Use the oneTime binding behaviour as much as possible. It allows you to tell Aurelia’s binding system to display a binding value and then forget it. A one-time binding will not update if the value changes, which can lead to great application performance.

If you would like to see examples of binding behaviours and how to work with them, Aurelia core team member has a great write-up on working with binding behaviours here which I recommend you also read.

Batch DOM changes using the Task Queue

The Aurelia Task Queue is a powerful module that allows you to sort of hook into the browsers event loop which allows you to perform DOM actions in an efficient way.

While slow Javascript code can definitely affect performance, it is a known fact that the DOM is the bottleneck of any web application, which is why libraries like React and Vue leverage a virtual DOM. Working with the DOM inefficiently can result in poor browser performance.

By queuing up a microtask, we can batch changes in the page resulting in less reflow and repaints.

import {TaskQueue} from 'aurelia-task-queue'; 

export class MyViewModel {
    static inject = [TaskQueue];

    constructor(taskQueue) {
        this.taskQueue = taskQueue;
    }

    attached() {
        this.makeSomeChanges();
    }

    makeSomeChanges() {
        this.taskQueue.queueMicroTask(() => {
            const header = document.getElementById('header');

            // Dynamically set the width and height of an element with ID "header"
            // We are batching DOM changes, so we only get on repaint/reflow
            header.style.height = '250px';
            header.style.width = '500px';
        });
    }
}

Be smart about event handling

In Aurelia, there are two ways to create event listeners. You can create an event listener the Javascript way using addEventListener or you can also bind to events from within your views with one such example being click.delegate="myCallback()".

Where possible, you should use Aurelia’s templating for binding to events. The biggest advantage being Aurelia cleans up after itself and removes events when they’re no longer being used.

This is not to say that addEventListener is completely redundant, but for eventing around elements in your page, you can and should use Aurelia’s provided way of working with native events.

You’ll have the piece of mind that events are properly being unbound when views are being destroyed, it’s like programming languages that automatically manage memory for you like PHP.

Don’t abuse the Event Aggregator

Aurelia comes with a great Event Aggregator module which allows you to publish and subscribe to events from within your application. The routing module also uses the Event Aggregator, firing off various events at different stages of the routing process.

As great as the Event Aggregator is, I have seen it abused quite a lot. I have done a bit of Aurelia consulting and in most instances, the Event Aggregator is being used for things it shouldn’t be used for.

The biggest issue you will encounter with the Event Aggregator is that it is hard to debug. This rings true for most custom eventing, not just Aurelia’s module.

So, when are you supposed to use the Event Aggregator then?

Do’s

  • Use the Event Aggregator to notify parts of your application about specific actions; user added, recorded deleted, a user logged in, a user logged out, etc.
  • Use the Event Aggregator to react to specific actions; a modal opening or closing, a user is deleted or if the user logs out clearing out a token in localStorage, etc.

Do nots

  • Don’t use the Event Aggregator for passing data between routes
  • Don’t use the Event Aggregator for passing data between components
  • Never treat the Event Aggregator as a crucial dependency (creating components that rely on specific events and values)

Passing data between routes

A common scenario I have witnessed in the community is wanting to route from Route A to route B and pass some data to the route you are navigating too.

I’ve seen developers pass data using the Event Aggregator (see above, don’t do that), use localStorage (also, don’t do that) and the even crazier: storing it on the route object itself (seriously, don’t do that).

The way that I always recommend passing data between routes and components is by using a singleton class. If you come from an Angular background, then you might know this as a service class.

Assuming you have two routes: Route A and Route B, and in Route A the user can enter their name and then be navigated to RouteB which will display their name.

I would achieve this in the following way:

    import {AppService} from './app-service';

    export class RouteA {
        static inject = [AppService];

        constructor(appService) {
            this.appService = appService;
        }
    }

    <template>
        <h1>Welcome: Route A</h1>
        <label>Please enter your name: <input type="text" value.bind="appService.name"></label>
    </template>

    import {AppService} from './app-service';

    export class RouteB {
        static inject = [AppService];

        constructor(appService) {
            this.appService = appService;
        }
    }

    <template>
        <h1>Hello, ${appService.name}</h1>
        Welcome to RouteB. The name you provided on the previous page is displayed above.
    </template>

    // Assuming this file is saved as app-service.js alongside the other route view-models
    export class AppService {
        constructor() {
            this.name = '';
        }
    }

Or better still, use a state management solution if your application state is complex and requires a much more manageable solution. The Aurelia Store is my favourite and recommended state management solution for Aurelia applications.

Why I Prefer Aurelia Over Angular, React & Vue

Let’s acknowledge the elephant in the room, if you’re a reader of my blog then you would know that I am a huge Aurelia fan and user. I’ve been working with Aurelia since February 2015, so 3.5 years now (wow).

I am on the Aurelia core team, I have contributed to the community through plugins/skeletons, answering questions on StackOverflow and I regularly do Aurelia consulting/freelance work (hire me). There is a good chance you’re reading this blog post because you’re familiar with my many previous Aurelia blog posts over the years.

Now we’ve got that stuff out of the way, I want to talk about why I continue to use Aurelia in the face of other choices.

Do you only use Aurelia?

No. It might surprise readers of my blog to know that I don’t always exclusively use Aurelia. Before Aurelia, I worked with ReactJS as well as Angular 1.x.

Since 2015 I have worked with React, Ember, Angular and Vue.js. In fact, about a year ago I launched a side project idea I had which is built using Vue + VueX called TidyFork. It allows you to clean up old Github forks and starred repositories.

A few months ago I helped a client migrate an Angular project over to Aurelia and it was an absolute breeze. I also participated in an internal effort to rewrite a React dashboard over to Aurelia.

In spite of not intentionally shunning other choices, I always come back to Aurelia.

Conventions

This is one of my favourite things about Aurelia and honestly, it surprises me that no other framework or library has really leveraged the convention over configuration approach. Specifically, in Angular, the use of decorators are promoted heavily and it results in overly verbose looking view-models.

Maybe it’s more of a personal preference thing, but I hate typing. I am always trying to find ways to macro my workflow and automate it. I really hate typing, especially the same thing over and over again.

If I want to create a custom element or attribute in Aurelia, all I do is name my view-model accordingly: MySpecialCustomElement or MySpecialCustomAttribute and I know Aurelia will look for the CustomElement and CustomAttribute parts to determine what kind of component this is.

I like the fact that I do not have to tell Aurelia the name of my accompanying view .html file and that it will just look for a file of the same name with the .html extension, but provide me with the option with telling Aurelia the name (if conventions can’t find it).

And the cool thing about conventions is that they can be changed. You can have the best of both worlds, let Aurelia make assumptions or tell Aurelia how you want it to work through decorators or class properties.

Reactive Binding (vDOM not included)

I’ve worked with React and Vue, I am quite familiar with the concept of a Virtual DOM and let me tell you, the Virtual DOM really irks me when people see it as a silver bullet for performance.

Aurelia doesn’t have a Virtual DOM and you probably wouldn’t even notice a difference if you compared two identical apps side-by-side (one built with React and the other Aurelia) because the reactive binding system offers many of the same benefits.

Look, there are definitely instances where Virtual DOM can be fast and it’s not slow. However, in most cases a reactive approach to binding is practically the same, it’s working with the DOM at isolated targeted points.

Be smart, don’t buy into the hype that Virtual DOM is the one true path to performance world.

Small Learning Curve

All frameworks and libraries I’ve used have boasted about their small learning curves when in reality it’s subjective and dependent on the developers skill-level and ability to learn new things.

I have first-hand experience with upskilling front-end and full-stack developers with ranging backgrounds. I have upskilled full-stack developers with no framework/library experience and developers with experience of frameworks like Angular or libraries like jQuery.

Every developer I have upskilled has grasped the basics of Aurelia within a couple of days, and that is being somewhat hands-off. The hardest part developers face is how to structure their applications and what to name things (which isn’t an Aurelia problem).

This is where the benefit of conventions come into play. If the framework makes decisions for you, there is less to learn upfront to start being productive.

You don’t need to hire “Aurelia developers”

This is one thing that really irks me about other frameworks and libraries, they make you buy into their way of doing things. React has its JSX syntax, Vue has its single-file components and Angular-like syntax. This leads companies to hiring framework and library-specific developers, I’m sure you’ve seen a job listing for a React developer before.

I’ve seen companies say they’re trying to hire Aurelia developers when Aurelia is so un-framework like all you need to do is hire a competent front-end/Javascript developer who will work out how to work with Aurelia in a day or two, easily.

Yes, Aurelia still has a little bit of buy-in with its templating language, but even that tries to follow a convention-like intuitive approach, tapping into that HTML/Javascript muscle of your brain.

Small, but passionate community

In my experience, most open source communities are hostile and/or toxic. While Aurelia has an acknowledged smaller community, it has a community and people are always around in the public chat (including Aurelia core team members). How many open source projects allow you to interact with the team building it? Not many.

Don’t buy into the FUD that Aurelia has a nonexistent community, we are here.

Unconditionally open source

After the ReactJS patents and the licencing fiasco that tainted React’s image, it really highlighted just how truly open source Aurelia is. It’s a community-led effort with many of the core team besides Rob and a few other early members being invited from the community itself.

You can use Aurelia for whatever you want, without worrying about infringing on some shadow companies legal terms or losing your licence to build applications with it.

Conclusion

I think any modern choice is great and I think regardless of whether or not you choose Angular, Aurelia, React or Vue, you can’t make a bad choice.

Keep an open mind about the frameworks and libraries you use, don’t follow the crowd and be a sheep: make your own choices.

Choose what works for you, Aurelia works for me.

Aurelia Firebase Server-side Rendering Skeleton Template

As many of you know, I love working with Aurelia and I love working with Firebase. I have been combining both of these together to create personal projects for a while now.

Newly introduced server-side rendering also strengthens my toolbelt with the ability to quickly spin up new Aurelia applications without having to worry about configuration and tooling.

This is why I have open sourced my Aurelia Firebase SSR Skeleton on Github. A starter for creating Aurelia applications, think of it as being similar to the skeleton applications Aurelia currently offers, but more up-to-date and easier to work with.

This starter is more verbose than what you might be used to, and it assumes some ways of working with Aurelia. Keep in mind that this skeleton is not for everyone, you might not want to use TypeScript or work with Firebase. If so, then this is not for you.

Features

  • Setup to work with TypeScript
  • Authentication (email/password and social auth sign in via Google) using Firebase
  • Firebase’ FireStore for the database
  • Setup for routing by default
  • Server-side rendering (including initial state passed from server to client)
  • Setup for state management using Aurelia Store

The skeleton also makes for a great example application showing you how to work with the great Aurelia Store plugin, especially with remote data from Firebase. And although this skeleton assumes a few things, it is based on the Aurelia CLI and easy to change to work with say a REST API or other use cases.

Why?

This is very much to scratch my own itch. I seem to only work with Firebase and Aurelia these days, so an application setup with some defaults out-of-the-box works well for me and even if only as a learning exercise, this skeleton should serve as a prime example of how to work with some cool technologies and build an Aurelia application.

If you are also curious about server-side rendering in Aurelia, then this is a great showcase setup which not only shows you how to get it working but also how to use the Aurelia Store plugin and pass initial state from the server through to the client.

Go forth.

Aurelia CLI Is Now Webpack First

One of my favourite Aurelia changes isn’t a new feature, nor is it a long-awaited bug fix or new tool: it’s the fact the CLI now supports Webpack by default.

Why is this a big deal? Previously, the CLI took a RequireJS first approach to bootstrapping new applications, meaning if you didn’t choose to customise your au new generated application, you would get RequireJS as your default bundler.

Now, there isn’t anything wrong with RequireJS as a bundler, but the issue with RequireJS is given its age there are issues dealing with different file formats, especially styles and static assets like images and fonts (which it cannot handle).

Whereas, Webpack can handle every single file format type in existence without blinking an eye. If you need to support it, either Webpack supports it out-of-the-box or you can download a file loader to add in support and a simple module rule definition from within your webpack.config.js file.

While RequireJS can handle CSS and Javascript (it’s showing its age even in this department), it cannot handle a lot of other formats, which is why the RequireJS CLI apps would use Gulp and some various build tasks to copy things like fonts and images.

The performance of Webpack in comparison to RequireJS? There is no comparison, Webpack might be more daunting, but it wins in speed (especially Webpack 4).

Aurelia: CLI versus Skeletons

I have been asked this question a few times and it can be hard to see things from the perspective of a newcomer to Aurelia, given how long I have been working with it. What is obvious to me is not obvious to everyone else.

When you’re deciding to build an Aurelia application, you’ll discover there is a CLI tool called Aurelia CLI and on Github, a collection of different skeleton applications for building an Aurelia application (preconfigured with testing, some routes and other logic).
 
The answer to this isn’t scientific, you should choose the CLI. The skeletons are representative of a time when Aurelia didn’t have a CLI, when it was still in alpha, beta and release candidate stages.

The CLI exists to replace the skeletons as a starting base for a new Aurelia application. You get to decide what bundler you want to use, what language you write in, whether or not you want testing and so on.

Going forward the already quite flexible CLI is going to see some great improvements, namely dropping shortly is Webpack being the default bundler of choice and a few other various improvements (including Webpack 4).
 

What if I want to use the skeletons?

 
The skeletons are still a fine starting base for building Aurelia applications off of. However, they seemingly get updated less than the CLI does and they require knowledge of configuring build tools and processes if you want to change things like preprocessors or languages.

I think the skeletons serve a great purpose of showing you how a basic Aurelia application with routing and HTTP requests is put together, as well as integrating libraries like Bootstrap, jQuery and Font Awesome.

If you do decide to choose one of the skeletons, be prepared to do some work to remove a bunch of dependencies and code you do not need in your app. In comparison, the CLI gives you an app that has nothing in it (except the required framework and tooling dependencies).

Working With Linked Node Modules & Webpack In Aurelia

When you are building an Aurelia plugin and you want to test it, you will want to use npm link or yarn link to create a local symbolic link to your module and then use it in a test application.

If you are working with Aurelia and Webpack, then you’ve probably run into an issue with linked Node modules can’t resolve files correctly. You might have some code in your plugin that looks like this in an index.js / index.ts.

export function configure(config) {
  if (config.globalResources) {
    config.globalResources([
        PLATFORM.moduleName("./my-plugin-file"),
        PLATFORM.moduleName("./another-plugin-file"),
        PLATFORM.moduleName("./some-file")
    ])
  }
}

To get Aurelia and Webpack working with symbolically linked modules, you need to set the NODE_PRESERVE_SYMLINKS environment variable. Regardless of whether or not you are on a Unix based OS like Linux or macOS, or Windows.

But if you link your built module on your local machine, then you’ll encounter an error that like this: Unhandled rejection Error: Error invoking SVGAnalyzer. Check the inner error for details. Message: WEBPACK_IMPORTED_MODULE_1_aurelia_pal.b.createElement is not a function

This error will only show up for locally linked Aurelia plugins. If you were to actually publish this to Npm where the node_modules directory wouldn’t be around, there wouldn’t be a problem.

You will probably try everything to fix it, you’ll start playing around with modules in your plugin and app. But the problem isn’t your plugin or your app, it’s the node_modules directory in your linked plugin.

It took me a long time to actually work out what was going on. As a short-term fix, I build the plugin and then I go into the linked packages node_modules directory and delete all of the Aurelia dependency folders.

I know it’s not ideal, but it fixes the issue. For some reason, Webpack will parse your node_modules directory in your linked package as well as the node_modules directory in the root of your application.

If/else In Aurelia Using The “else” Attribute

Did you know Aurelia introduced an else attribute a while ago which allows you to do if/else statements in your views complete with support for animation?

If you have been working with Aurelia for more than a few months, then you probably have been working with if/else statements by using multiple if.bind attributes in your HTML or using a terniary inside of string interpolation curlies. But, the else attribute allows us to write cleaner HTML without the need for multiple if statements doing the inverse of one another.

Not only does the else attribute simplify template control syntax, it also supports animation via the Aurelia Animator, thanks to the swap binding parameter added to if you can choose how your else element moves into place with the existing if.

Let’s start with a simple example:

<div if.bind="myCondition">This is truthy</div>
<div else>This is falsy</div>

This is the else attribute in use. It adds an inverse if.bind to your DIV based on the previous condition.

Previously you might have written that like this:

<div if.bind="myCondition">This is truthy</div>
<div if.bind="!myCondition">This is falsy</div>

This isn’t completely horrible, but else is arguably easier to write and look at. You want to avoid bloating your views at all costs. Plus, animating the transition between those two elements would be difficult and not possible with Aurelia out-of-the-box.

If/else with animation:

This is my favourite thing about using if and else together: animation. The if binding supports a binding parameter (we mentioned above) called swap which dictates how the if and else coordinate with one another when animation.

Supported values for swap are:

  • before – Animation is triggered before element is removed
  • with – Animation smoothly syncs up with both elements
  • after Animation is triggered after if condition

One thing you need to keep in mind is you need to add au-animate to the element that has the else attribute. The example below showcases how you can use swap with if and else to animate the change.

<div if="condition.bind: myCondition; swap: with">Truthy :)</div>
<div else class="au-animate">Falsy :(</div>

If you want to change the animation mode, just replace with in the above example with any of the valid options.

Caveats

You might be tempted to try different combinations of if and else but the else attribute is intentionally limited. There aren’t too many caveats, just a couple you need to be aware of to avoid frustration.

  • You cannot chain else attributes ie. Multiple else statements.
  • The else attribute cannot be used with if on the same line. This will not work: else if.bind="condition"
  • The element containing the else attribute needs to come after the element with if

Webpack Support Lands In Aurelia CLI

I have been waiting for this day to come for a long time now: the Aurelia CLI now supports scaffolding Webpack applications from scratch.

My biggest gripe with the skeletons the Aurelia team provide is they have a lot of stuff in them I have to remove for every new project.

Admittedly, the skeletons serve to showcase how a functional Aurelia application could look like — but make a lot of assumptions on things like using a router or Fetch to make HTTP requests.

Heavily driving the support for Webpack is core team member Jeroen Vinke who has made countless contributions and improvements to the CLI (amongst a few other developers).

To start scaffolding Webpack applications, make sure you update your CLI by installing the latest version: npm install -g aurelia-cli and going through the au new wizard process.

The CLI still lacks the ability to update your Aurelia applications using older versions of the CLI, so starting a new project and copying your files across is still the current approach for doing so (for now).

Checking If a View Slot Is Defined In Aurelia

Aurelia supports the <slot> element provided via the HTML Web Components specification, which allows you to define placeholders in your HTML that can be replaced.

A lot of examples around seem to wrap a slot with a DIV, perhaps a class. The issue with this approach is if you have a styled DIV wrapping a slot and that slot is empty, your DIV will still be affecting the space around it. This is where the ability to check if a user has provided a slot or not helps.

This post was prompted by a StackOverflow question I answered here. It dawned on me this isn’t a straightforward solution and other people might find it valuable.

Take the following fictional example of a modal custom element. We have a header, body and footer. The body contains a slot without a name, but the header and footer are named slots.

<template>
    <div class="modal__header">
      <h1>Header:</h1>
      <slot name="modalHeader"></slot>
    </div>
    
    <div class="modal__body">
      <h1>Body:</h1>
      <slot></slot>
    </div>
    
    <div class="modal__footer">
      <h1>Footer:</h1>
      <slot name="modalFooter"></slot>
    </div>
</template>

The issue here as I mentioned earlier is a) styles on wrapping DIV’s affecting the layout even if the slot is empty and b) if the element wrapping your slot contains other HTML with your slot, it will show up even if no slot content is provided (as can be seen above).

What we want to do is conditionally check if a slot has user-provided content and show or hide it accordingly.

Accessing the controller instance

Aurelia creates an au property on every tracked Aurelia node in the page, containing its view factory information, bindings and more. By accessing the controller and then the view property of our controller, we can access our slots.

export class ModalCustomElement {
  static inject = [Element];
  
  constructor(element) {
    this.element = element;
  }
  
  attached() {
    this.$slots = this.element.au.controller.view.slots;
    console.log(this.$slots);
  }
}

Inside of the view-model for our fictional modal custom element, we inject the custom element instance and then we create a class property called $slots where we pass in the view controller slots property containing our slots.

In our example, the $slots property we created should contain three slots. Because the $slots property is an object which is keyed by slot name, in our view we can determine if the user has provided slot data by checking the children property.

This then gives us some HTML that looks like this:

<template>
    <div class="modal__header" show.bind="$slots.modalHeader.children.length">
      <h1>Header:</h1>
      <slot name="modalHeader"></slot>
    </div>
    
    <div class="modal__body">
      <h1>Body:</h1>
      <slot></slot>
    </div>
    
    <div class="modal__footer" show.bind="$slots.modalFooter.children.length">
      <h1>Footer:</h1>
      <slot name="modalFooter"></slot>
    </div>
</template>

For each slot, we check the children property which is an array. We know if the length is zero, the user hasn’t provided any data for this slot. Default data inside of a slot will not be counted in the children array, that lives elsewhere in the fallback slot property.

We are also accessing the slot via its name as it lives in the object. For the default slot with no provided name, its name internally is: __au-default-slot-key__ which can also be checked using a show.bind.

Why if.bind and not show.bind?

You’ll notice we are using show.bind and not if.bind above. You can’t actually use if.bind because it removes the slot from the controller and therefore, we can no longer check for it. When using show the element remains but is hidden and our slot still exists.

Working example

If the above went over your head, the example I posted in the SO question is here where you can see DIV’s being shown/hidden depending on whether or not the user provides slot content.