Angular and Aurelia, descendants of good old JavaScript Angular 1, are fierce competitors, developed and released at approximately the same time and with a similar philosophy, but differing in a number of key ways. In this article, we’ll do side-by-side comparisons of those differences in features and code.

Long story short, Aurelia was created by Rob Eisenberg, known as the creator of Durandal and Caliburn. He worked on the Angular 2 team at Google but left in 2014 when his views on how a modern framework should look differed from theirs.

The similarities persist on a more technical level, too: Templates and the components (or custom elements) associated with them are core to both Angular and Aurelia apps, and both require you to have a root component (i.e. the app). Furthermore, both Angular and Aurelia heavily use decorators for component configuration. Each component has a fixed lifecycle that we can hook into.

So What’s the Difference between Aurelia and Angular 2?

The key difference, according to Rob Eisenberg, is in the code: Aurelia is unobtrusive. When developing an Aurelia app (after configuration), you write in ES6 or TypeScript, and the templates look like absolutely sane HTML, especially when compared to Angular. Aurelia is convention over configuration, and 95% of the time you’ll be just fine using default conventions (such as template naming, element naming, etc.), while Angular requires you to provide configuration for basically everything.

Aurelia is also considered to be more standards compliant, if only because it is not case-sensitive when it comes to HTML tags, whereas Angular 2 is. This means that Angular 2 cannot rely on the browser’s HTML parser, so they created their own.

Another factor to consider when choosing between SPA-frameworks is the community- the ecosystem- around them. Both Angular and Aurelia have all the basics (router, template engine, validation, etc), and it’s easy to get a native modal or use some third-party library, but it comes as no surprise that Angular has a bigger community and bigger development team.

Furthermore, while both frameworks are open source, Angular is mainly developed by Google and is not intended to be commercialized while Durandal, Inc., which employs the core team, is following Ember.js’ model of monetization via consulting and training.

Aurelia vs. Angular: A Code Comparison

Let’s look at some of the most notable features which underline the philosophies behind each framework.

After cloning seed projects for Angular and Aurelia, we have a ES6 Aurelia app (you can use Jspm/System.js, Webpack and RequireJS in combination with ES6 or TypeScript) and a TypeScript Angular app (WebPack), respectively.

Let’s roll.

Data Binding

Before we compare side by side working examples, we have to take a look at some syntactical differences between Aurelia and Angular 2, namely in the key functionality of binding values from the controller to the view. Angular 1 used “dirty checking” for everything, a method which scanned the scope for changes. This, understandably, caused a number of performance issues. Neither Angular 2 nor Aurelia followed that path. Instead, they use event binding.

Data Binding in Angular 2

In Angular, you bind data with square brackets and use parentheses to bind events, like so:

<element [property]="value"></a>
<element (someEvent)="eventHandler($event)"></a>

Two-way binding—for when you want changes in application data to reflect on the view and vice versa—is a combination of both square and parenthesis brackets. So, for two-way bound input, this would work just fine:

<input type="text" [(ngModel)]="text">
{{text}}

In other words, parentheses represent an event while square brackets represent a value being pushed to input.

The Angular team did a great job separating binding directions: to the DOM, from the DOM, and bidirectional. There’s also a lot of syntax sugar related to binding classes and styles. Consider, for example, the following snippet as an example of one-way binding:

<div [class.red-container]="isRed"></div>   
<div [style.width.px]="elementWidth"></div> 

But what if we want to bind two-way data into a component? Consider the following basic input setup:

<!-- parent component -->
<input type="text" [(ngModel)]="text">
{{ text }}

<my-component [(text)]="text"></my-component>

import {Component, Input} from '@angular/core';

@Component(/* ... */)
export class MyComponent {
  @Input() text : string;
}

<!-- child component -->
<input [(ngModel)]="text">
Text in child: {{ text }}

Note that, to use ngModel, your module must import FormsModule from @angular/forms. Now we have something interesting. Updating the value in the parent input changes values everywhere, but modifying the child’s input only affects that child. If we want it to update the parent value, we need an event informing the parent. The naming convention for this event is property name + 'Change', like so:

import {Component, Input, Output, EventEmitter} from '@angular/core';

@Component(/* ... */)
export class MyComponent {
  @Input() text : string;
  @Output() textChange = new EventEmitter();

  triggerUpdate() {
    this.textChange.emit(this.text);
  }
}

The two-way binding starts working properly right after we bind to the ngModelChange event:

<!-- child component -->
<input [(ngModel)]="text" (ngModelChange)="triggerUpdate($event)">

What about one-time binding, when you effectively tell the framework to ignore the bound values even if they change?

In Angular 1, we used {{::value}} to bind one time. In Angular 2, one-time binding gets complicated: the documentation says that you can use the changeDetection: ChangeDetectionStrategy.OnPush attribute in the Component’s configuration, but that will make all your bindings one-time.

Data Binding: The Aurelia Way

In contrast to Angular 2, binding data and events in Aurelia is really simple. You can use either interpolation, just like Angular’s property="${value}", or use one of the following binding types:

property.one-time="value"
property.one-way="value"
property.two-way="value"

The names are self-explanatory. Furthermore, there’s property.bind="value", which is syntax-sugar that self-detects whether the binding should be one-way or two-way. Consider:

<!-- parent-->
<template bindable="text">
  <input type="text" value.bind="text"/>
  <child text.two-way="text"></child>
</template>

<!-- child custom element -->
<template bindable="text">
  <input type="text" value.bind="text"/>
</template>

In the above snippet, both @bindable and @Input are configurable, so you can easily change things like the name of the property being bound, etc.

What about events? To bind to events in Aurelia, you use .trigger and .delegate. For example, to have a child component fire an event, you might do the following:

// child.js
this.element.dispatchEvent(new CustomEvent('change', {
   detail: someDetails
}));

Then, to listen for that in the parent:

<child change.trigger="myChangeHandler($event)"></child>
<!-- or -->
<child change.delegate="myChangeHandler($event)"></child>

The difference between those two is that .trigger creates an event handler on that particular element whilst .delegate adds a listener on document. This saves resources, but it obviously won’t work for non-bubbling events.

A Basic Example of Aurelia vs. Angular

Now that we’ve covered binding, let’s create a basic component which renders a scalable vector graphic (SVG). It’ll be awesome, therefore we’ll call it awesome-svg. This exercise will illustrate both basic functionality and philosophy for Aurelia and Angular 2. This article’s Aurelia code examples are available on GitHub.

SVG Rectangles Example in Aurelia

Let’s first build out the JavaScript file:

// awesome-svg.js

import {bindable} from 'aurelia-framework';

export class AwesomeSvgCustomElement {
  @bindable title;
  @bindable colors = [];
}

Now for the HTML.

In Aurelia, you can specify the template (or use the inline one) with the annotations @template, @inlineView or even the @noView, but out of the box, it searches for the .html file with the same name as the .js file. The same is true for the custom element’s name—you can set it with @customElement('awesome-svg'), but if you don’t, Aurelia will convert the title to dash-case and look for a match.

Since we didn’t specify otherwise, the element will be called awesome-svg and Aurelia will search for the template with the same name as the js file (i.e. awesome-svg.html) in the same directory:

<!-- awesome-svg.html -->

<template>
  <h1>${title}</h1>
  <svg>
    <rect repeat.for="color of colors" 
          fill.bind="color" 
          x.bind="$index * 100" y="0" 
          width="50" height="50"></rect>
  </svg>
</template>

Notice the <template> tag? All templates need to be wrapped in a <template> tag. Also worth noticing is that you use ` for … of and the string interpolation ${title}` just like you do in ES6.

Now to use the component, we should either import it in a template with <require from="path/to/awesome-svg"></require> or, if it’s used across the app, globalize the resource in the framework’s configure function with aurelia.use.globalResources('path/to/awesome-svg');, which will import the awesome-svg component once and for all.

[Note, if you do neither of these, <awesome-svg></awesome-svg> will be treated just like any other HTML tag, without errors.]

You can display the component with:

<awesome-svg colors.bind="['#ff0000', '#00ff00', '#0000ff']"></awesome-svg>

This renders a set of 3 rectangles:

SVG Rectangles Example in Aurelia

SVG Rectangles Example in Angular 2

Now let’s do the same example in Angular 2, also available on GitHub. Angular 2 requires that we specify both the template and the element name:

// awesome-svg.component.ts

import {Component, Input} from '@angular/core';

@Component({
  selector: 'awesome-svg',
  templateUrl: './awesome-svg.component.html'
})
export class AwesomeSvgComponent {
  @Input() title : string;
  @Input() colors : string[] = []
}

The view is where things get a little bit complicated. First of all, Angular silently treats unknown HTML tags the same way a browser does: it fires an error saying something along the lines of my-own-tag is an unknown element. It does the same for any properties you bind, so if you had a typo somewhere in the code, it would attract major attention because the app would crash. Sounds good, right? Yes, because you’d notice right away if you broke the app, and no, because this is just bad form.

Consider this snippet, which is perfectly fine in terms of binding syntax:

<svg>
  <rect [fill]="color"></rect>
</svg>

Even though it reads fine, you’ll get an error like “can’t bind to ‘fill’ since it isn’t a known property of ‘:svg:rect’.” To fix this, you need to use the syntax [attr.fill]="color" instead. Also note that it’s required to specify namespace in child elements inside <svg/>: <svg:rect> to let Angular know this should not be treated as HTML. Let’s expand our snippet:

<!-- awesome-svg.component.html-->

<h1>{{ title }}</h1>
<svg>
  <rect
    *ngFor="let color of colors; let i = index"
    [attr.fill]="color"
    [attr.x]="i * 100" y="0"
    width="50" height="50"
  ></rect>
</svg>

There we go. Next, import it in the module configuration:

@NgModule({
  declarations: [ AwesomeSvgComponent ]
  //...
})

Now the component may be used across this module, like so:

<awesome-svg [colors]="['#ff0000', '#00ff00', '#0000ff']" title="Rectangles"></awesome-svg>

SVG Rectangles Example in AngularJS 2

Custom Elements

Suppose now we want our rectangle code to be a custom component with its own logic.

Custom Elements: The Angular 2 Way

Since Angular 2 renders components by what matches its defined selector, it’s extremely easy to define a custom component, like so:

@Component({
    selector: 'g[custom-rect]',
  ...
})

The above snippet would render the custom element to any <g custom-rect></div> tags, which is extremely handy.

Custom Elements: The Aurelia Way

Aurelia lets us create template-only custom elements:

<template bindable="colors, title">
  <h1>${title}</h1>
  <svg>
    <rect repeat.for="color of colors" 
          fill.bind="color" 
          x.bind="$index * 100" y="0" 
          width="50" height="50"></rect>
  </svg>
</template>

The custom element will be named with respect to the file name. The only difference from naming other components is that when importing, either in configure or via the <require> tag, you should put .html at the end. So for example: <require from="awesome-svg.html"></require>.

Aurelia has custom attributes, too, but they do not serve the same purpose as in Angular 2. For example, in Aurelia, you can use the @containerless annotation on the custom rect element. @containerless can also be used with custom templates without the controller and <compose>, which basically renders stuff into the DOM.

Consider the following code containing the @containerless annotation:

<svg>
  <custom-rect containerless></custom-rect>
</svg>

The output would not contain the custom element tag (custom-rect), but instead we get:

<svg>
  <rect ...></rect>
</svg>

Services

In regards to services, Aurelia and Angular are very similar, as you’ll see in the following examples. Suppose we need NumberOperator which depends on NumberGenerator.

Services in Aurelia

Here’s how to define our two services in Aurelia:

import {inject} from 'aurelia-framework';
import {NumberGenerator} from './number-generator'

export class NumberGenerator {
  getNumber(){
    return 42;
  }
}

@inject(NumberGenerator)
export class NumberOperator {
  constructor(numberGenerator){
    this.numberGenerator = numberGenerator;
    this.counter = 0;
  }

  getNumber(){
    return this.numberGenerator.getNumber() + this.counter++;
  }
}

Now, for a component, we inject in the same way:

import {inject} from 'aurelia-framework';
import {NumberOperator} from './_services/number-operator';

@inject(NumberOperator)
export class SomeCustomElement {
  constructor(numberOperator){
    this.numberOperator = numberOperator;
    //this.numberOperator.getNumber();
  }
}

As you can see, with dependency injection, any class can be a fully extensible service, so you can even write your own resolvers.

Factories in Aurelia

If what you need is a factory or a new instance (Factory and NewInstance are just a couple popular resolvers provided out of the box), you can do the following:

import { Factory, NewInstance } from 'aurelia-framework';

@inject(SomeService)
export class Stuff {
  constructor(someService, config){
    this.someService = someService;
  }
}

@inject(Factory.of(Stuff), NewInstance.of(AnotherService))
export class SomethingUsingStuff {
  constructor(stuffFactory, anotherService){
    this.stuff = stuffFactory(config);
    this.anotherServiceNewInstance = anotherService;
  }
}

Angular Services

Here’s the same set of services in Angular 2:

import { Injectable } from '@angular/core';
import { NumberGenerator } from './number-generator';

@Injectable()
export class NumberGenerator {
  getNumber(){
    return 42;
  }
}

@Injectable()
export class NumberOperator {

  counter : number = 0;

  constructor(@Inject(NumberGenerator) private numberGenerator) { }

  getNumber(){
    return this.numberGenerator.getNumber() + this.counter++;
  }
}

The @Injectable annotation is required, and to actually inject a service, you need to specify the service in the list of providers in the component configuration or the entire module configuration, like so:

@Component({
  //...
  providers: [NumberOperator, NumberGenerator]
})

Or, not recommended, you can also specify it in the bootstrap(AppComponent, [NumberGenerator, NumberOperator]) call.

Note that you need to specify both NumberOperator and NumberGenerator, regardless of how you inject it.

The resulting component will look something like this:

@Component({
  //...
  providers: [NumberOperator, NumberGenerator],
})
export class SomeComponent {
  constructor(@Inject(NumberOperator) public service){
    //service.getNumber();
  }
}
Factories in Angular 2

In Angular 2, you can create factories with the provide annotation, which is also used for aliasing services in order to prevent name collisions. Creating a factory might look like the following:

let stuffFactory = (someService: SomeService) => {
    return new Stuff(someService);
}

@Component({
  //...
  providers: [provide(Stuff, {useFactory: stuffFactory, deps: [SomeService]})]
})

Transclusion

Angular 1 had the ability to include content, a “slot,” from one template into another using transclusion. Let’s see what its descendants have to offer.

Content Projection with Angular 2

In Angular 2, transclusion is called “content projection,” and it works the same way that ng-transclude did; i.e., speaking in Angular 1 terms, the transcluded content uses the parent scope. It will match the transcluded content tag based on the configuration selector. Consider:

@Component({
  selector: 'child',
  template: `Transcluded: <ng-content></ng-content>`
})
export class MyComponent {}

You can then use the component with <child-component>Hello from Translusion Component</child-component>, and we’ll get the exact transcluded Yes HTML rendered in the child-component.

For multi-slot transclusion, Angular 2 has selectors that you can use in the same manner as you would for @Component configuration:

<!-- child.component.html -->
<h4>Slot 1:</h4>
<ng-content select=".class-selector"></ng-content>

<h4>Slot 2:</h4>
<ng-content select="[attr-selector]"></ng-content>
<!-- parent.component.html -->


<child>
  <span class="class-selector">Hello from Translusion Component</span>
  <p class="class-selector">Hello from Translusion Component again</p>
  <span attr-selector>Hello from Translusion Component one more time</span>
</child>

Slots in Angular2

You can use select on your custom tags, but remember that the tag must be known to Angular 2.

Slots with Aurelia

Remember when I said Aurelia follows web standards whenever possible? In Aurelia, transclusion is called slots, and it’s just a polyfill for Web Components Shadow DOM. Shadow DOM is not created for slots yet, but it follows W3C specs.

<!-- child -->
<template>
  Slot: <slot></slot>
</template>

<!-- parent -->
<template>
  <child>${textValue}</child>
</template>

Aurelia was designed to be standards-compliant, and Angular 2 wasn’t. As a result, we can do more awesome things with Aurelia’s slots, like use fallback content (trying to use fallback content in Angular 2 fails with <ng-content> element cannot have content). Consider:

<!-- child -->
<template>
  Slot A: <slot name="slot-a"></slot> <br />
  Slot B: <slot name="slot-b"></slot>
 Slot C: <slot name="slot-c">Fallback Content</slot>
</template>

<!-- parent -->
<template>
  <child>
    <div slot="slot-a">A value</div>
    <div slot="slot-b">B value</div>
</child>
</template>

In the same fashion as Angular 2, Aurelia will render all occurrences of the slot based on a name match.

Slots in Aurelia

It’s also worth noting that in both Aurelia and Angular, you can compile template parts and render components dynamically (using <compose> with view-model in Aurelia, or ComponentResolver in Angular 2).

Shadow DOM

Both Aurelia and Angular support Shadow DOM.

In Aurelia, just use the @useShadowDOM decorator and you’re ready to go:

import {useShadowDOM} from 'aurelia-framework';

@useShadowDOM()
export class YetAnotherCustomElement {}

In Angular, the same can be done with ViewEncapsulation.Native:

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  //...
  encapsulation: ViewEncapsulation.Native,
})
export class YetAnotherComponent {}

Remember to check if your browser supports Shadow DOM.

Server-side Rendering

It’s 2017, and server-side rendering is very trendy. You can already render Angular 2 on the back-end with Angular Universal, and Aurelia will have this in 2017 as stated in its team’s New Year’s resolutions. In fact, there’s a runnable demo in Aurelia’s repo.

In addition to that, Aurelia has had progressive enhancement capabilities for over a year, something that Angular 2 does not because of its non-standard HTML syntax.

Size, Performance, and What’s Coming Next

While it doesn’t show us the whole picture, DBMonster benchmarks, with default configurations and optimized implementation, paints a good comparison picture: Aurelia and Angular show similar results of approximately 100 re-renders per second (as tested on a MacBook Pro), while Angular 1 showed something around half of that result. Both Aurelia and Angular outperform Angular 1 by about five times, and both are 40% ahead of React. Neither Aurelia nor Angular 2 have a Virtual DOM implementation.

On the issue of size, Angular is roughly twice as fat as Aurelia, but the guys at Google are working on it: Angular’s roadmap includes releasing Angular 4 with plans to make it smaller and more lightweight while improving developer experience. There’s no Angular 3, and really, the version number should be dropped when talking about Angular, since major releases are planned every 6 months. If you look at the path Angular took from alpha to its current version, you’ll see that it wasn’t always coherent with things like attributes being renamed from from build to build, etc. The Angular team promises that breaking changes will be easy to migrate.

As of 2017, the Aurelia team plans to release Aurelia UX, provide more integrations and tools, and implement server-side rendering (which has been on the roadmap for a very long time).

Angular 2 vs. Aurelia: A Matter of Taste

Both Angular and Aurelia are good, and choosing one over another is a matter of personal taste and priorities. If you need a more slender solution, Aurelia is your best option, but if you need community support, Angular is your winner. It’s not a question of “Does this framework allow me to…?” because the answer is simply “yes.” They provide roughly the same functionality while following different philosophies and styles along with a completely different approach to web standards.

About the author

Alexander Mikhalchenko, Belarus
member since June 11, 2015
Alexander is a true full-stack developer and he's proven himself to be the guy that can swoop in to rescue a project. He's fine with tight schedules and overnight prototyping even though he prefers less extreme development. Alexander is also a fast learner and a empathetic communicator. [click to continue...]
Hiring? Meet the Top 10 Freelance AngularJS Developers for Hire in May 2017

Comments

brianm101
aggh! - Reminders me of Romeo and Juliet 'a curse on both your houses' (Angular and Aurelia ) Why web development has to be so hard is beyond me - we need a standard framework that kills off all these private efforts which are full of opinions (which are usually over inflated!) etc. Interesting article though - and to show my total unbiased view Aurelia wins on clarity and simplicity!
Vytautas Pranskunas
It would be nice if you touch change detection for Aurelia and Angular
Matthew Wilson
Not a bad article, but it seems a bit ill-timed with the recent release of Angular 4. To be honest, I can't even think of a reason why you would cite anything related to Angular 1. Angular 2 (and version 4 moving forward) are a complete rewrite of 1. And version 4 negates the issue of file size as they broke up the many libraries to make the distro more lean. I'm not trying to throw shade on Aurelia (other than that name - good grief! It doesn't exactly roll off the tongue), but I think it's WAY too early to discount Angular. Once they did a rewrite in version 2, and promised to deliver updates frequently, and major releases twice a year - they are definitely a force to be reckoned with.
Rayhan Ali
I would treat angular with a grain of rock size salt now they way they broke angular 1 and still haven't learned a lesson. Migration is easy? Who told them that? Even minor project ends up costing millions of dollars.! As long as Google maintains this arrogant attitude I would be keeping away from their code even if it is free. The truth is that angular1 proved quite expensive for thousands of projects. Also, history shows that one man vision is far far better than bunch of corporate engineers.! I will always evaulate Aureila first. In 2013 late I recommended angular 1 simply because evaluating itself cost us huge and we decided to continue. Ended up costing me my job and millions to the customer.! Job was easy to get because there are lots of stupid project managers out there who will fall to your feet the moment they hear Google but I had really liked my position in the previous company and the freedom they provided. And then angular 2 ended up breaking everything.!
Matthew Wilson
Rayhan - I truly empathize with your situation. Anyone "caught" in the middle between Angular 1 and Angular 2 will definitely feel the pain. However, Angular 2 is solid - and I think it deserves a bit of credit. It's no different than all those developers who built their careers on Flash...only to have the rug pulled out from beneath them with HTML5. And I can only imagine the 100s of millions that switch cost people. But that's how Tech works. Don't get too comfortable - you never know what tomorrow holds.
Noitidart
"Reminders me of Romeo and Juliet 'a curse on both your houses" Hahaha so true i felt the same after reading this. Also after reading this I appreciate God even more for leading me away from Angular 1.x to React!!
Abdur Rahman Warriach
I totally agree with you, don't get too comfortable with any tool, I was expert in Flash and hated other languages, but then forced to learn them. So, he lesson is, don't made any idle in tech World.
Rayhan Ali
I think we are talking different things. Another better example is CMP promoted as a part of J2EE and supported by big corporations. I need not tell how cleaner and better ORM compared to CMP.! There are many examples which clearly shows not to trust corporate engineers when open source alternative is there.! And nothing is free. Real cost is always in maintaining code and that can make or break projects if backward compatibility and domain driven design is not followed. I see that angular team continues to treat backward compatibility issues as an afterthought. That is red flag. Though good news for outsourcing firm becuase they can reduce costs on paper. I have used ExtJS, dojo, backbone etc. I mostly work on middle tiers and also project management. So, don't assume that I got comfortable with angular 1. It was just a terrible decision. It is not comfort. It is cost of migrating from angular 1 to 2 and probably later. And the delays because of useless migration so early.
Herbert Wraczlavski
I think if you have a luxury of being an independent developer, the safest bet is to write your own lightweight, custom framework.
Dustin Davis
Nice analysis of each language. I was a big fan of Angular 1. When they announced ng2 I really wanted to like it, but I threw up in my mouth when I saw their template syntax. I went in search of an alternative and I've been happy as a clam at high tide developing in Aurelia the past year and a half. I don't like their pink and purple logo and color scheme and I do wish the community was bigger. I think that is the biggest pain point. It's much like when I was trying to learn Django pre 1.0.
WE ARE HUMAN
Two major release every year is not good in term of maintaning your project. who wants to fix deprcated api bugs every six months? The other thing is, no one knows the direction that angular is heading to. It is what the angular team sees right.
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant Technologies
About the author
Alexander Mikhalchenko
PHP Developer
Alexander is a true full-stack developer and he's proven himself to be the guy that can swoop in to rescue a project. He's fine with tight schedules and overnight prototyping even though he prefers less extreme development. Alexander is also a fast learner and a empathetic communicator.