This article assumes you have been working with Angular 1, and especially es5, for a while and felt the pain points. The idea is to first describe the problems surrounding Angular 1 one which ultimately led to the creation of Aurelia. Lastly I will try to highlight Aurelias approach to solving these problems, and hopefully convince you that Aurelia is elegant and worth trying out.

Pain points

Writing an SPA is easy when the features are few and the codebase is relatively small. At this point it doesn’t really matter what framework you use wether it is knockout, angular, react, ember or just plain vanilla javascript. However as your application grows so grows the need for being able to organize your code in a way that makes it easy to remember where you put things like models, services or other artefacts.

Enter Angular 1

When angular was released it was greeted as the savior. Developers felt productive using it. They were able to produce working applications in very little time. This was the case for a long time but soon developers started to complain about performance and that business logic was too tightly coupled with the framework.

Angular 1 - the good parts

Angular forced your code to be organized according to an MVC pattern, even though they called it model view whatever, using keywords such as controllers, services, factories, which meant your code suddenly was organized in neat layers. It was also easy to remember different constructs as everything was called ng something. But the real selling point was double binding. It made it so easy to create master-detail like views. Another great selling point was testability. Pretty much everything could be tested which meant it could be taken seriously in enterprise contexts. And of course this brought about a revolution in the js world, suddenly everyone wanted to do testing.

Angular 1 - the bad parts

The thing that sold angular to the masses was also the things that was turned against it. As the size of your application grew so did the number of problems related to performance, but also it made the app harder to reason about, who owns the data, where to put validation and so on. The part on performance was blamed on how Angular 1 did change detection. To put it short it basically checked the whole world as soon as a property was changed.

Also developers suddenly started to think that what if I angular wouldn’t exist anymore how would I be able to yank out all that business code from the framework?

Another big critique was that angular was perceived as hard to master by the sheer amount of concepts one had to learn.

Finding a way forward

Of course devs are crafty and managed to find a decent solution to most of these problems. One time bindings was used more and more to counter performance problems created by using too many watchers. People starting reasoning that maybe double binding was a bad approach generally and that maybe uniform data flow, that React was using, was a better approach to the problem.

Also devs started to look at solutions such as browserify, introducing commonjs modules, was a good way of separating business logic from the framework.

Next generation

Despite the fact that solutions were found and are nowadays applied to most Angular 1 applications, devs as well as the angular core team saw the need to reinvent Angular 1. The number of reasons was quite a lot but a few important ones were :

  • pluggability
  • a need to rewrite change detection
  • decrease the number of concepts
  • make a modern language like es2015 or typescript a first class citizen, rather than something that is possible to do but hurts a little as is the case with Angular 1, i.e time needs to be spent with grunt/gulp/webpack/jspm before you have a setup you really like

Enter Aurelia

Rob Eisenberg the creator of Durandal was invited to work on the Angular core team and with Angular 2. It was found that Durandal did a lot of things well and Robs ideas for a Durandal 2 started to converge with that of Angular 2. For a time they were all working on creating Angular 2 trying to address the pain points mentioned.

Then Rob decided to leave the Angular core team and instead create Aurelia.

Solving the pain points

I mentioned earlier there were pain points in need of solving.

Decrease the number of concepts

So what were all theses concepts that people had a hard time to grok?

Factory and services

People spent a lot of time trying to discern the difference between a service and a factory. Service and factory was used in a similar way but had subtle differences. Bottom line was one of them would have been enough.

Angular 1 - solution

Declaring the service

angular
    .module('app')
    .service('service', function($http){
        this.getData = function(){
            return $http.get('route');
        }
    })

Consuming the service

angular
.module('app')
.controller('controller', function(service){
    service
        .getData()
        .then(function(response){
            $scope.data = response.data;
        })
})     

Aurelia - solution:

Aurelias answer to this was to NOT make services and factories a part of the framework at all. The solution was to write vanilla javascript classes and use the fact that es2016 came with its own dependency injection. Let me illustrate by a code example of a service in Aurelia

Declaring the service

export class Service{
    constructor(){
        
    }
    
    getData() {
        // do http call
    }        
    
    performCalculation() {
        
    }
}   

Consuming the service

@inject(Service)
class Component{
    constructor(service) {
        this.service = service;
       
    }
    
    loadData() {
        this.service
                .getData()
                .then(response => {
                    this.data = response.data;
                })
    }
}

NOTE: @inject is an Aurelia specific wrapper over es2016 injection code looking like this

static inject(){ return [ Service ]; }

Directives and controllers

The usage for directives in Angular 1 was to create reusable controls and the controllers job was to be the first responders to route change and create a view model the view could render. So what could be done about those? The angular core team with Rob decided to remove both these concepts and replace it with just one concept a component. The idea was to make a component both the responder of a route change but also possible to use as a reusable control.

So the following Angular 1 directive

Declaring it:

angular
    .module('app')
    .directive('test',{
        restrict : 'E',
        scope : {
            prop : '=',
            prop2 : '='
        }
        template : `  `,
        controller : function($scope){
            
        }
    })

Using it

<test prop="val" prop2='val2'></test>

Would be replaced by code in Aurelia looking like this:

Declaring it:

class Test{
    constructor)(){
        @bindable prop;
        @bindable prop2;
    }
}

<template>
     
</template>    

Using it:

<test prop="val" prop2='val2'></test>

Bootstrapping

Bootstrapping is mentioned as this is something angular one did really well. You just had to write

<div ng-app="app">
    1
</div> 

And you had a running application

A fear that more than one developer has when moving from one framework to another, is that it will get to hard to bootstrap and therefore you don’t change framework, it is better with the enemy you know.

Todays modern framework tends to require you to run

npm install

And/or

bower install 

and have a real complex setup in webpack or gulp just to spit out “hello world”.

On this point Aurelias shines. Here is what it looks like after you installed jspm:

<body aurelia-app>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
        System.import("aurelia-bootstrapper") //app.js
    </script>
</body>
        
// app.js

export class App{
    constructor(){
        this.message = "hello from aurelia";
    }
}

// app.html

<template>
    
</template>

Bindings

In Angular 1 bindings was a pretty crucial thing to get right to ensure your app remained performant. For one-way binding you typed

 or ng-bind or ng-bind-template

The same in Aurelia is

${ prop }  or  <div innerHTML.bind=prop></div>

Angular 1 two way binding was

<input ng-model="prop">

The same thing in Aurela looks like

<input value.bind="prop" >              

Another thing you needed to know about in Angular 1 was how to do one-time bindings to ensure those values was set once.

You would write:

In Aurelia you would use the oneTime keyword

<input data-original.one-time="name" value.bind="name" > or ${ prop & oneTime }     

So one-way and two-way binding has the same syntax in Aurelia, on less thing to remember. One-time binding is different though.

Constant/Value

With Angular 1 you would have to keep track of the bootstrap process. .constant would happen first and .value would happen after providers were bootstrapped. In Aurelia these concepts simply doesn’t exist. We simply create a const like so and import it for usage

export const myConstant = 33; 

Filter

Aurelia like Angular 1 has two types of filters. Value filters and array filters. Filters are created by creating a class ending with ValueConverter having a toView method with a suitable signature, like so:

export class DateFormatValueConverter {
  toView(value) {
   return moment(value).format('M/D/YYYY');
  }
}

Usage:

${todoItem.created_at | dateFormat}    

And of course the array filter would be similar

export class FilterValueConverter {
    toView(array, searchTerm, filterFunc) {
        
        return array.filter((item) => {
        
        let matches = searchTerm && searchTerm.length > 0 ? filterFunc(searchTerm,item): true;
                        
        return matches;
        });
    }
}

Usage:

<tr repeat.for="todoItem of todoItems | filter:searchTerm:filterFunc">

And filterFunc would be a filter function on the hosting component with the job of determining wether that row should be imcluded or not:

filterFunc(searchExpression, value){
 
   let itemValue = value.text;
 
   return itmeValue.contains( searchExpression ):
}            

So filter functionality is there but easier on the memory. Name your class correctly and include a toView method. OK but what about the performance haunting Angular 1 filters, is that still an issue?

Nope, filters are fast.

Pluggability

Aurelia is very easy to extend. If you define a configuration file for bootstrap it is easy to add your plugin. Like so:

<body aurelia-app="main">
    ....
</body>

Means we point to main.js

export function configure(aurelia) {
    aurelia.use
        .standardConfiguration()
        .developmentLogging()
        .plugin('pluginName'); 

    aurelia.start().then(a => a.setRoot('app', document.body));
}     

The following row is what adds the plugin to the core

.plugin('pluginName');

Here are some examples of plugins that can be added.

Authentication

Aurelia with flux

Sometimes a plugin can replace an implementation and sometimes just add to the core. To read more about Aurelias pluggability here is a link to an article by its creator Aurelia pluggability

Regardless it is by first installing a plugin with jspm install name and then do .plugin in config file that allows you to extend Aurelia.

Change detection

In short The best strategy is chosen depending on the features of the browser and the type of object/property being observed.

Here is more detailed article on the subject Change detection

Summary

So the selling points of Aurelia are

  • You barely notice its there as you tend to code most things in es2015 rather that relying on knowledge of keywords.
  • It is modern , written in es2015/es2016.
  • It’s easy to get started with, very little setup.
  • It reduces the number of concepts to remember greatly compared to Angular 1.
  • It is using a smarter approach to change detection by choosing the better strategy depending on what and browser support.
  • It is pluggable, very easy to add plugins to it, add your own or someone elses.