JavaScript
Article

Creating a Next Gen JavaScript Application with Aurelia

By Brad Barrow

2015 brings with it the finalization of the ECMAScript 6 specification and with that the confidence to build modern, superior applications in JavaScript.

The current landscape of JavaScript frameworks is dominated by the recognizable giants AngularJS and React both of which are aiming in some way, shape or form, to incorporate new ES6 features into their paradigms.

There is however, another player that while new and relatively secretive, looks elegant in its use of modern JavaScript features. I’d like to take a moment to introduce you to Aurelia.

Aureli-who?

Aurelia is a next generation framework that leverages modern concepts like ES6, Web Components, and modularization to help you develop performant, futureproof applications.

Aurelia is the natural progression of Durandal, an AngularJS competitor built by Rob Eisenberg. Aurelia’s history involves a number of encounters with the AngularJS team over the years. It’s for this reason that many aspects of the framework might feel familiar to the AngularJS developers among you.

New Technologies

As I said, Aurelia is a “next generation” framework and as a consequence the tools it uses may be new to some of you. It runs on Node.js, and uses npm but it relies on a few cool new pieces of tech that we’ll look at briefly below:

Gulp

This one isn’t so new but it’s a core part of Aurelia’s setup. We’ll use Gulp to pipe all our files through various tasks to ensure our application is all wired up and ready to go.

ES6 Module Loader Polyfill

The ES6 module loader is a pollyfill for the System dynamic module loader that was part of the original ES6 specification. The System loader is in the process of being written into browser specifications but in the meantime this polyfill provides a futureproof solution that we can use today.

The loader allows us to dynamically load modules defined in the ES6 module syntax using the System.import method:

System.import('mymodule').then(function(m) { ... });

In addition to loading ES6 modules, the loader allows to load other module syntaxes through the use of hooks.

SystemJS

With its slightly confusing name, SystemJS is essentially a collection of loader hooks for the ES6 module loader that enable us to load modules from npm, jspm, ES6 Modules and more. You can think of it as a feature rich module loader built on the future proof foundation of the ES6 Module Loader Polyfill.

jspm

jspm is a package manager, like npm, designed to be used with SystemJS. It allows us to install packages from various sources and exposes those to our app so we can easily import them with SystemJS.

Let’s Get Set up

I’m going to assume you’ve already installed Node.js, npm and Git, and that you’re familiar with the use of all of them.

We’ll start by cloning the Aurelia example application repository from GitHub

git clone https://github.com/aurelia/skeleton-navigation.git

At this point you might ask: “Why are we cloning their example app rather than starting our own from scratch?”
The reason is that Aurelia is still in an early stage, thus there’s no simple aurelia init command yet that you can run to get your package.json file and everything set up.

The repository we cloned acts as a good base for our app. It gives us a directory structure, a package manifest, some testing configuration and more. Hopefully one day there’ll be an installer of sorts or we’ll defer to generators like Yeoman the setup. Since we’re using the repository for its configuration and not for their example app itself, you can go ahead and delete the src/ directory, and the styles/styles.css and index.html files. We’ll create our own shortly.

We’ll need to install a few other things in order to install our dependencies and kick start our app:

Install gulp globally so that we have access to the gulp CLI:

npm install -g gulp

Then, install jspm globally for the same reason.

npm install -g jspm

Now open the CLI and move to your app’s root directory. Once done, run the command:

npm install

It’ll install our dependencies (from the package.json file) that include among other things:

  • Aurelia tools
  • Gulp plugins
  • Karma packages for testing

Once the process is completed, we’ll install our jspm packages as well using the command:

jspm install -y

This is the bit that actually installs the modules that include Aurelia.

Last but not least, let’s install Bootstrap with jspm:

jspm install bootstrap

It’s worth noting that the Aurelia library (contained within these modules) has a number of dependencies on its own, including SystemJS. These will all be installed through dependency management as a result of installing Aurelia itself. I wanted to highlight this point just in case you’re wondering how we have access to things like SystemJS later on despite not having listed it explicitly here in our dependencies.

Time to build an app

We’ve now got a host of tools to help us build our app. What we need next is an index.html page:

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="jspm_packages/github/twbs/bootstrap@3.3.4/css/bootstrap.min.css">
    <link rel="stylesheet" href="styles/styles.css">
  </head>
  <body aurelia-app>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
      System.config({
        "paths": {
          "*": "dist/*.js"
         }
      });
      System.import('aurelia-bootstrapper');
    </script>
  </body>
</html>

Let’s step through the contents of <body>.

As I mentioned before, SystemJS allows us to use the System.import method. In this code, we use it to import the aurelia-bootsrapper module which kicks off our Aurelia app. We can reference aurelia-bootstrapper by name thanks to the config.js file that jspm built for us when we ran jspm install -y. It maps the module name, to its versioned source. Pretty nifty stuff.

The System.config bit sets up the paths for our modules, i.e. where to start looking for files.

Now, create the styles/style.css file and add this code to it:

body { padding-top: 74px; }

You’ll notice that we’re including Bootstrap which we installed earlier. The version may have changed at the time you read this tutorial, so take note of which one jspm installed.

What does the aurelia-bootstrapper do?

The aurelia-bootstrapper module will scan the index.html file for an aurelia-app attribute. If such attribute specifies a value, then the bootstrapper will load the view/module with that name; otherwise it’ll load a view and module called app.html and app.js (which are the defaults). The view will get loaded into the element that has the aurelia-app attribute (in this case the <body> tag). It’ll be wired up to the app.js file.

Let’s create an app.js and app.html file in the src directory to see this in action:

export class App {
  constructor() {
    this.name = "Brad";
  }
}
<template>
  Hello, my name is <strong>${name}</strong>
</template>

The first thing you’ll notice is the use of the new ES6 module syntax and the export keyword. You’ll also notice the use of the new ES6 class syntax and abbreviated function signatures. Aurelia, thanks to SystemJS, comes with support for many exciting ES6 features straight out of the box.

Here we see that app.js defines a class whose properties are exposed as variables for use in the app.html file. This class is known as a view-model, since it’s a data structure that backs our view. We print out the variables in our template using ES6 string interpolation syntax.

As the last note, I want to highlight that all the templates in Aurelia are wrapped in a <template> tag.

Viewing our application in a browser

To get the app up and running in a browser, all we need to do is execute the command:

gulp watch

That’ll do all the magic of compiling ES6, live reload, and so on. You should be able to see your app at http://localhost:9000/. As we expected, we see the contents of our template rendered inside the <bodygt; tag and we see the property interpolated into the template.

Our gulpfile has already setup BrowserSync for us so the page will reload if you make any changes.

Time to build our app

In this section, we’ll build a naive Reddit client that has two pages: “Funny” and “Gifs”. We’ll fetch data for each page from Reddit’s API and display a list on each page.

When building any application with multiple pages, the core of the application is the router and Aurelia is no different. Let’s change our app.js file, so that it becomes the core module of our app. It’ll be responsible for defining and configuring routing.

import {Router} from "aurelia-router";

export class App {
  static inject() { return [Router]; }

  constructor(router) {
    this.router = router;
    this.router.configure(config => {
      config.title = "Reddit";

      config.map([
        {route: ["", "funny"], moduleId: "funny", nav: true, title: "Funny Subreddit"},
        {route: "gifs", moduleId: "gifs", nav: true, title: "Gifs Subreddit"}
      ]);
    });
  }
}

So, what have we done here?

The first line (import {Router} from "aurelia_router") imports the router itself using ES6 module import syntax.

Then, in the App class we have a static function called inject. Those of you familiar with AngularJS and not only will already know about dependency injection. The inject function is going to determine, via dependency injection, what parameters will be available in our constructor function. In this instance, a single parameter will be provided and that’s our router. You can see we’ve altered the constructor function to accept that new parameter.

Dependency injection is powerful because it allows the loose coupling of modules and hands the control flow up a level meaning we can swap out those dependencies during testing or later on when they’re updated.

Now that we have the router available in the constructor of our class, we can use it to set up the routes.

First and foremost we set the router as a property of the class itself with this.router = router;. This is an Aurelia convention and is necessary for routing to work. Note that naming is important in this instance.

Secondly, we configure our routes by using the config object provided to us in the callback of this.router.configure. We set a title property that will be used to set the title of our pages. We also pass a list of route definitions to the config.map function.

Each route definition has the following pattern:

{
  route: ["", "foo"], // Activate this route by default or when on /foo
  moduleId: "foo", // When active, load foo.js and foo.html (module)
  nav: true, // Add this route to the list of navigable routes (used for building UI)
  title: "Foo" // Used in the creation of a pages title
}

So, in our instance we’ve got two pages that we can visit at /#/funny and /#/gifs, with /#/funny acting as our default page thanks to the ["", "funny"] list of two route patterns.

We’ll also need to update app.html to act as our app’s layout file.

<template>
  <a href="/#/funny">Funny</a>
  <a href="/#/gifs">Gifs</a>
  <router-view>
  </router-view>
</template>

Can you see the <router-view></router-view> custom element? This is another built-in piece of Aurelia’s features. You can think of it like an AngularJS directive or just a web component. The view associated with the current route will automatically be loaded into this element.

Next, we’ll need to define the two modules: funny and gifs.

Writing our page modules

The “Funny” module

We’ll start with funny and then copy it over as a basis for gifs.

Create a /src/funny.js file with the following content:

import {HttpClient} from 'aurelia-http-client';

export class Funny {
   // Dependency inject the HttpClient
  static inject() { return [HttpClient]; }

  constructor(http) {
    this.http = http; // Assign the http client for use later
    this.posts = [];
    this.subreddit_url = "http://reddit.com/r/funny.json";
  }

  loadPosts() {
    // Aurelia's http client provides us with a jsonp method for
    // getting around CORS issues. The second param is the callback
    // name which reddit requires to be "jsonp"

    return this.http.jsonp(this.subreddit_url, "jsonp").then(r => {
      // Assign the list of posts from the json response from reddit
      this.posts = r.response.data.children;
    });
  }

  // This is called once when the route activates
  activate() {
    return this.loadPosts();
  }
}

Also create /src/funny.html as follows:

<template>
  <ul class="list-group">
    <li class="list-group-item" repeat.for="p of posts">
      <img src.bind="p.data.thumbnail" />
      <a href="http://reddit.com${p.data.permalink}">
        ${p.data.title}
      </a>
    </li>
  </ul>
</template>

The “Gifs” module

Let’s simply copy our funny.js and funny.html to src/gifs.js and src/gifs.html respectively. We’ll need to tweak the contents of gifs.js a little.

import {HttpClient} from 'aurelia-http-client';

export class Gifs {
  static inject() { return [HttpClient]; }

  constructor(http) {
    this.http = http;
    this.posts = [];
    this.subreddit_url = "http://reddit.com/r/gifs.json";
  }

  loadPosts() {
    return this.http.jsonp(this.subreddit_url, "jsonp").then(r => {
      this.posts = r.response.data.children;
    });
  }

  activate() {
    return this.loadPosts();
  }
}

Now you should be able to visit localhost:9000/#/gifs to see a list of gif posts and their links.

Improvements to our layout

We can make a couple of improvements to our layout template using Aurelia’s router.

Remember the nav:true property we set in our route config earlier? What it does is to add a route to a list that we can iterate over in our view in order to build dynamic navigation. Let’s do that now.

Update the contents of app.html as follows:

<template>
<div class="container">
  <ul class="nav navbar-nav navbar-fixed-top navbar-inverse">
    <li repeat.for="navItem of router.navigation" class="${navItem.isActive ? 'active' : ''}">
      <a href.bind="navItem.href">
        ${navItem.title}
      </a>
    </li>
  </ul>

  <router-view></router-view>
</div>
 </template>

Conclusion

Well there you have it! Your first Aurelia application. I’m pretty excited about the future of Aurelia as I think it’s clean and straightforward. Moreover, by using ES6 it keeps everything in reusable, extendable modules. In future tutorials, I’ll look at how we can abstract the duplication between the Gifs and Funny modules, as well as some other improvements and additions to our Reddit client. I’d love to know how your first attempt at app development with Aurelia goes!

The complete application that we’ve built during this article can be found here

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

Comments
joezim007

I like that Aurelia is a "convention over configuration" type of framework, and I'm glad that they don't have quite so much logic in the templates (my biggest annoyance with Ember and Angular), but I still think that there's a little too much logic in there. Sometimes there are developers who work on JavaScript that are separate from the devs who work on HTML and CSS, so templates should avoid logic to allow those markup people to use them without needing to know anything about the app logic or learn all the nuances of the templating language.

Also, I was looking at the GitHub repo and saw the setup instructions, which ask you to install 2 global modules: Gulp and JSPM. I have an article about this: http://www.joezimjs.com/javascript/no-more-global-npm-packages/. Global modules should be avoided, and should instead be installed as dev-dependencies, because then you can store version information and you can use NPM Scripts to handle all the work for you. In your case, you probably should have saved Gulp and JSPM as dev-dependencies and then create an NPM script names "setup" or something that runs "npm install && jspm install -y" and then create an NPM script called "start" or "develop" and have it run "gulp watch". That way, steps 2-5 are compressed into 1 step and then if the app changes to use a different system (other than Gulp) for starting the app, it can be transparent to the use because they never needed to install the global deps and never had to type "gulp ..." into their command line.

Also, try not to view this as a stuck up programmer trying to correct you for doing something "wrong". I'm just trying to spread the word about what I (and many others) have come to see as a best practice and prevent the spread of what we consider to be bad practice. I hope you'll consider it.

s_molinari

@joezim007 - could you give some examples of too much logic in the templates?

As far as I am concerned, I need two types of logic. Data binding/replacement and HTML/DOM manipulation. Aurelia does both well and more pragmatic than Angular from what I can tell. But, I am far from an expert in such matters too. So I'd like to know what you think is to much logic. Thanks.

Scott

joezim007

1) I think that event handling should be taken out of the DOM (e.g. <form submit.delegate="welcome()">
2) Loops and other control structures just don't seem right when used as attributes of an element rather than on their own. (e.g. <li repeat.for="el of collection">) This isn't about too much logic, just strangely done logic.

It's not nearly as bad as Angular and Ember who use templates to do a lot more, but I'm used to Backbone and Ampersand where you have more of the logic in the classes rather than in the template. Templates should be dumb so that they can be mostly written by non-JS devs.

Starx

Can this only be used in nodejs? Or can be ran on PHP Stack?

s_molinari

Aurelia is a frontend JS framework. It has nothing to do with the server side.

Scott

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in JavaScript, once a week, for free.