Hello Aurelia!

Getting Started

In this post I want to give you a quick intro to building SPAs with Aurelia, which will hopefully whet your appetite to dive deeper, which my course Aurelia Fundamentals on Pluralsight will help you do. The Aurelia team has a lot of good getting started materials out there in the docs, but I thought I would give you my spin on getting started with Aurelia from my perspective.

I'm going to use ES2015/ES2016 for this post because that is what I used throughout my Pluralsight course Aurelia Fundamentals and I think it is a little easier for people who have never spent time looking at TypeScript but do have JavaScript experience to grok. But TypeScript is definitely something you should consider diving into in conjunction with learning Aurelia. There is a great deal of overlap between ES2015/ES2016 and TypeScript. So if you know one it is actually pretty easy to read code from the other. TypeScript just layers on some additional and important capabilities such as strong typing, generics, and class member visibility. Also, theoretcially you can write Aurelia apps just using plain ES5 compliant JavaScript, because when you use the Babel transpiler and polyfills that I'll show in this post and go into more detail on in my course, you end up generating ES5 from your ES2015/2016 code either at build or runtime. But if you go compare that code with the ES2015/16 code that it came from, I think you will quickly start to realize why you won't want to write Aurelia code in ES5. I'm referring to it as ES2015/16 because you should use a number of ES2015 features for defining the code structure for your components in Aurelia, but you will also use a couple of ES2016 features - specifically decorators and possibly class properties in standard Aurelia coding as well. Aurelia always has a path where you can just use the ratified ES2015 features and use some alternate syntax instead of decorators, but the decorators make the code much more readable. So lets dive right in using ES2015/16.

If you are starting from scratch, you first need to get a tool chain in place for working with ES2015/16. One path covered by the Aurelia docs is to use their Yeoman code generator to scaffold out an entire project and build and test tools. I think that approach is great once you get comfortable with Aurelia and how it works, but I think it is a little daunting to get started for someone coming from a little bit of JS background and maybe a little SPA experience with other frameworks like Angular or Knockout. I think it is good to start with an empty folder and build your way up from there.

Prerequisites

There are a couple of pre-requisites we need to get started. The first is that you need to have NodeJS on your machine. You don't have to host your app with node, but the tools we will be using are all NodeJS based command line tools. So make sure you have a fairly recent version of Node installed. As of this post, the recommended version is 4.4.4, but everything I show here will work fine back to 0.12 versions of Node. Node also installs npm, which we will use to pull down some of the other things we need.

So create an empty folder somewhere on your machine called helloaurelia, and open a command or Powershell prompt on Windows or Terminal on MacOS in that folder. The first thing we are going to do is install JSPM - JSPM is a package manager that can be used to not only pull down Aurelia and other JS Libraries, it sets up the ES2015 module loading system that you need and also wires up the transpiler you will use to either run your ES2015/16 during development, or transpile it in a build step for dev or deployment.

So the first step is to drop into your empty helloaurelia folder and run this command:

npm init  

When you run that, it will ask a series of questions. You can just accept the default on all of them by hitting enter, or fill them out as you choose. This will look like this.

npm init screen

Once you are done it will create a package.json file in the folder with the values from your answers. If you want you can trim it down to just the bare essentials:

{
  "name": "helloaurelia",
  "version": "1.0.0",
  "description": "",
  "author": "Brian Noyes"
}

Next up, we need to get JSPM, since Aurelia uses that for package management, the system ES2015 module loader, and pulling in a transpiler to handle the ES2015/16 for browsers that do not support it yet. So next you can type this into the command prompt/terminal:

npm install -g jspm  

This will install JSPM globablly so that you can use its command line anywhere on your machine. If you are on a Mac and you have not set your environment up to not require sudo for npm global installs, then you may need to preface that with "sudo" on MacOS.

Once that is done, it may feel kind of redundant, but you should run:

npm install jspm --save  

This installs an instance of JSPM locally so your code can take a dependency on the local version and not potentially break from installs of newer versions of JSPM at the global level. Another way to think about this is the global install makes JSPM available as a command line tool anywhere on your machine. The local install lays down the module loader and transpiler/polyfills that you are going to take a direct dependency on from your code. The "--save" tells it to record that dependency into a package.json file that will be created in your root folder. Doing so means you can delete the nodemodules subfolder to hand your code off to someone else and mark nodemodules so it doesn't get checked into your source control because it can be recreated by just typing npm install in that root folder as long as that package.json is present saying what needs to be pulled down.

Now the package.json will look like this:

{
  "name": "helloaurelia",
  "version": "1.0.0",
  "description": "",
  "author": "Brian Noyes",
  "dependencies": {
    "jspm": "^0.16.34"
  }
}

And if you look in the nodemodules folder you will see a ton of packages that got pulled down as dependencies of JSPM. As I mentioned you can delete that nodemodules folder at any time and restore it by simply typing npm install at a command prompt in that folder where package.json lives.

Speaking of deleting node_modules - here is another important little tip - install rimraf:

npm install -g rimraf  

Then anytime you need to hand your code off to someone else (don't do this now) you can delete node_modules with:

rimraf node_modules  

Not so important on Mac, but on Windows a lot of times the node_modules subfolder hierarchy gets too deep for Windows Explorer to handle and you will have trouble doing a normal delete of the folder, or at best it will take forever to do so. Rimraf is exceptionally fast and doesn't care how recursively deep the folder structure is. And again all it takes to restore that folder with the right modules is to run npm install with no arguments.

Next up you initialize JSPM:

jspm init  

This will likewise ask a series of questions, you can accept defaults for all of them by hitting enter other than the last one - after it asks if you wish to use a transpiler, and you accept the default of [yes] by hitting enter, if it does not default to Babel, you will want to type in Babel to use that transpiler.

After that last question, JSPM is going to pull down a bunch more packages for the transpiler and system module loader and place those into a jspmpackages folder. This too you don't need to check into source control and can delete at any time (again quicker to do so with rimraf) because all you need to do to reinstall all the dependent JSPM modules after getting rid of jspmpackages is to run:

jspm install  

This, like npm install, will go download all the modules again using the dependency chains that are put into package.json and config.js, generated by the jspm init command.

By now if you are used to seeing Hello World examples for other web frameworks that do not leverage ES2015/16 or TypeScript you are probably thinking "Holy crap, that is a lot of prerequisite knowledge and steps!". If so don't fret. If you want to start your apps from scratch like this it is really just a series of three commands:
1. npm init
2. npm install jspm --save
3. jspm init

And you can rip through those in less than a minute with a decent internet connection. Also keep in mind this is a one time thing when you create a new Aurelia project. Once this and the bootstrapping code I'll cover next is in place, you will just focus on building out your SPA with Views, ViewModels, and Services. And those are just names for Aurelia components that contain certain kinds of code.

You also have the option of just using the Aurelia generator with yo aurelia, which you can find more information about in my course or in the Aurelia Docs. There will also soon be project templates out there for development IDEs such as Visual Studio.

Commence Aurelia Coding!

While we are focused on command line stuff, next thing to do is pull down Aurelia for use in your app. To do that we use JSPM and pull down two aurelia modules:

jspm install aurelia-framework aurelia-bootstrapper  

You will always need the framework module, which pulls in a lot of other Aurelia modules as dependencies. You will also generally want the bootstrapper, which sets up the standard initialization process for Aurelia. But technically you could skip using that and write your own bootstrapping code against the framework. But ain't nobody got time for that. Unless you are doing something really specialized during startup of your app, in which case you wouldn't be reading this post, just plan on using the Aurelia Bootstrapper.

Now we can start writing our Hello Aurelia app. Aurelia apps are inherently Single Page Apps, or SPAs. Go ahead and create an index.html in the same folder with the following boilerplate code:

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

This code first pulls in the system.js module loader, then pulls in the config.js file that was generated by the jspm init step (and gets updated for each jspm module you install). Then it calls the first line of Aurelia specific code - the line that uses the System module to import the Aurelia bootstrapper. That kicks off aurelia and its application lifecycle. The first thing it is going to do is look for an element in the DOM that is marked with the aurelia-app attribute. You will put that on whatever element you want to be the root element of your SPA. Every element and attribute that is placed within that aurelia-app element will be evaluated by aurelia and be able to participate if desired in Aurelia data binding, routing, and other features.

Once you save that, you could try and run. To run our code in as lightweight a fashion as possible for this post, I am going to use a handy little Node command line utility called http-server. To get that on your machine, simply run the command:

npm install -g http-server  

Once you do you can start up a lightweight web server from the command line in any folder on your machine that can serve up static files - i.e. the HTML, CSS, and JS files that your SPA is composed of.

To run it, just run the following at the command line:

http-server -p 2112  

The -p argument is optional - it lets you listen on a particular port of your choosing. If you leave it off it will use 8080. I don't use that because I find other programs sometimes will use that for their own sockets or HTTP communication behind the scenes. Port 2112 has always worked fine for me and is a subtle homage to one of the best albums ever. But you can pick whatever port you want there.

Now you could fire up a browser and navigate to http://localhost:2112/index.html and you will get a wonderfully blank page! How exciting! But seriously, I have you try this at this point to emphasize another important aspect of Aurelia coding in general and in terms of the minimum bootstrapping code you need. If you open the browser developer console and take a look after firing up the page, you will see something like this:

INFO [aurelia] Aurelia Started  
http://localhost:2112/app.js Failed to load resource: the server responded with a status of 404 (Not Found)  
Error: Error: XHR error (404 Not Found) loading http://localhost:2112/app.js(…)  

The first line shows that in fact Aurelia has fired up and is starting to run your SPA. But then it goes looking for app.js. Why? We didn't do anything in our code that has anything to do with app.js... or have we?

An important concept or principle that Aurelia uses heavily is "convention over configuration". That means that a framework can have a well defined set of conventions for how it is going to work and what it is going to call, and then you don't have to write as much explicit configuration code to get it to do normal things. So the convention that is going on here is that Aurelia expects that you will be using the MVVM pattern for structuring your views and supporting interaction logic in ViewModels that are expressed as ES2015 or TypeScript classes defined in modules. When you do MVVM, you will typically have a root View that has its own ViewModel that contains the rest of the child Views, navigation, composition, etc being contained within it. So when that one line of JS in index.html imports the bootstrapper, and it goes looking and finds the aurelia-app attribute, the framework then expects to find app.js file in the root folder containing the definition of that root ViewModel, and an app.html that contains the root view element structure. Since we have not defined those yet, it is getting a 404 trying to download them.

With convention over configuration, you should always be able to override the conventions, and I show how to do that in my course. But for this post we will stick to the convention and add those files.

Adding the First MVVM View and ViewModel

So in the spirit of "Hello World", lets add the following to a new app.html file in our root folder:

<template>  
    <div>${message}</div>
</template>  

Aurelia views always have a template element as the root element. This element is part of HTML 5 capabilities and is designed specifically for the case where a dynamic chunk of HTML markup is going to be rendered into the DOM and may be later removed (such as when client side navigation or routing occurs) and provides a way to isolate that chunk of DOM from other parts of the DOM if needed. The ${message} is an Aurelia data binding expression that is based on the ES 2015 "template literal" or "template strings" feature. When used this way as a data binding expression, it is implicitly saying "I am an MVVM View, and I expect my ViewModel to expose a message property that I can data bind to. That means whatever value the ViewModel puts into that property will be rendered into that div, even if it changes over time.

So lets create a minimal ViewModel to support that View. Add an app.js file in your folder with the following code:

export class App {  
    constructor() {
        this.message = "Hello Aurelia!";
    }
}

Your app.js becomes your first ES2015 module that you define. Modules in ES 2015 make it so instead of everything you declare in your JS files going into the global namespace and possibly stomping on each other, declarations in a module are private to that module by default. Only if you use the export keyword with a variable, function, or class will it be accessible outside that module, and then only if it is explicitly pulled into scope with an import statement. For those that have Java or .NET experience, an import in ES2015 is like an import in Java or a using in .NET, it brings external declarions in scope within the module you are importing into. We are not going to use an import in this demo because we will only have a single View/ViewModel pair as a component. But you can learn all about modules, exports, and imports in my course along with the other important ES2015 and ES2016 features you will need for Aurelia development.

So app.js just contains a single class declaration (classes are also a new primary declaration type in ES2015 or TypeScript), and that class is exported from the app module. The class contains a constructor that declares a single member property named message. The properties and methods you declare on your ViewModel class can be directly data bound to or invoked from the View's markup with data binding expressions and Aurelia or custom attributes and elements.

With those two parts of the app component in place, you can save and run http-server again (if you even stopped it). And you should get something like this:
Hello Aurelia Running

Notice the text value put in the message property by the ViewModel gets rendered out as expected by the View's data binding expression. Also notice all the debug statements in the debugger console. The Aurelia framework has a built in and extensible logger that captures all the low level stuff going on in the framework, which can be very helpful when you get something wrong in your code that prevents Aurelia from being able to do what it normally does. You can also modify the logging level if you want through some custom configuration during startup, which I cover in my course.

As a last morsel before wrapping up this post, lets modify the ViewModel code to the following:

export class App {  
    constructor() {
        this.message = '';
    }

    activate() {
        setInterval(_ => {
            if (this.message === 'Hello') this.message = 'Aurelia';
            else this.message = 'Hello';
        }, 1000);
    }
}

The activate method is part of the standard lifecycle of a MVVM component in Aurelia. When the view is activated, either through composition with Aurelia's compose element, or through routing within a router-view, if you have an activate method on your ViewModel class it will be called. This makes it a good place to do initialization code that needs to run every time your view is made active. The code in this activate method demostrates that you can update the message property at any time and the data binding expression in the view will re-render the new value. There is no nasty digest cycle in the background enabling this like in Angular 1.x, and there is no special syntax to notify the binding to update like with knockout. And it is important to realize it is just updating that single binding when the underlying property changes, no matter how many other bindings are in the page.

This code also shows a couple of other important features in ES2015 - "Arrow functions" (aka lambda expressions for .NET people), and the fact that code inside an arrow function have access to the .this reference to the class instance and don't suffer the confusing aspects of using the .this reference in ES5 and prior JavaScript.

So that is a quick journey into getting started with Aurelia. For an end-to-end coverage of the capabilities of Aurelia, please check out my Aurelia Fundamentals course on Pluralsight.

You can download the completed code for this post here.