The Aurelia Tutorial Revisited

I’ve been working between a client-side SPA (for example, AngularJS) and classic server-side MVC (for example, ASP.NET MVC6) for a while now. The problem is that most of the frameworks aren’t friendly. They require me to use a generator of some description because they are horrendous to set up. With ES2015 now out, they are rushing to change – EmberJS is going slow and AngularJS is doing a complete rewrite and getting everyone to fork-lift upgrade. It’s not a pleasant time to be learning a framework. This is why I’ve moved towards Aurelia. First off, it’s based on ES6 and ES7, so the code is already future proof. Secondly, it’s a much more accessible framework. I can start with an empty project and know what I am getting into.

From a Visual Studio point of view, I have to deal with jspm and SystemJS. Despite what the authors say, it’s not friendly to RequireJS. JSPM has no support inside Visual Studio. With ES2015 support coming in the final release of VS2015, I’m hopeful it will be a better experience.

So, the investigation this week is this: Can I write an Aurelia App with authentication via Auth0 and pulling data from an ASP.NET MVC6 WebAPI utilizing authentication. Todays piece is a walk through of the Aurelia Tutorial within VS2015 using the latest code from each.

Setting up the Project

To start with, I created a blank ASP.NET5 application – it has a Project_Readme.html file (that I deleted), a Startup.cs file and a project.json file. I know that all the code I am going to write is going to go into wwwroot and be served up by the StaticFiles module. I also know that eventually I will be doing MVC work. In preparation for everything, here is my project.json file:

{
  "webroot": "wwwroot",
  "version": "0.0.1",

  "dependencies": {
    "Microsoft.AspNet.Diagnostics": "1.0.0-beta4",
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
    "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4"
  },

  "commands": {
      "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": { }
  },

  "publishExclude": [
    "node_modules",
    "bower_components",
    "**.xproj",
    "**.user",
    "**.vspscc"
  ],
  "exclude": [
    "wwwroot",
    "node_modules",
    "bower_components"
  ]
}

I’ve added the various dependencies that I need to support MVC and static files. Now for the Startup.cs file:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;

namespace aurelia_2
{
    public class Startup
    {
        /// <summary>
        /// Constructor - load the configuration files and store in the
        /// static area.
        /// </summary>
        public Startup()
        {
            Configuration = new Configuration()
                .AddJsonFile("config.json", optional: true)
                .AddEnvironmentVariables();
        }

        #region Application Configuration
        /// <summary>
        /// Static Configuration Details
        /// </summary>
        public static IConfiguration Configuration {
            get;
            private set;
        }
        #endregion

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseErrorPage(ErrorPageOptions.ShowAll);
            app.UseStaticFiles();
            app.UseMvc();

        }
    }
}

Again, this is all pretty much boiler-plate, and minimal at that. I need to set up npm and jspm. Npm can be set up by creating a package.json file:

{
  "name": "aurelia-2",
  "version": "0.0.1",
  "description": "An Aurelia 2 Web Application",
  "private": true,
  "repository": {
    "type": "git",
    "url": "https://github.com/adrianhall/blog-code/aurelia-2"
  },
  "author": {
    "email": "adrian@shellmogner.com",
    "name": "Adrian Hall"
  },
  "license": "MIT"
}

Jspm needs to be initialized on the command line. Run jspm init. I found the questions straight forward. The only one I had to enter anything other than accepting the default is the server baseURL. The default is ./ and ASP.NET requires ./wwwroot:

blog-0708-1

Don’t forget to also run jspm registry config github and enter your GitHub credentials if you haven’t already. This is a standard part of the setup for JSPM.

I also need an index.html file in the wwwroot:

<!DOCTYPE html>
<html>
<head>
    <title>Aurelia-2</title>
</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>

This is a copy of the HTML file at the start of the Aurelia Tutorial. Run the project at this stage and check out the Javascript console:

blog-0708-2

Well, I haven’t downloaded the aurelia system yet. If I was using the “generator” app, this would be done for me, but I’m not. Back on the console, I can enter the following:

jspm install aurelia-bootstrapper
jspm install aurelia-framework

When you run the project now, you get the following in the Javascript console:

blog-0708-3

It looks like the application is looking for a file called “app” – which means we are on the right track, because that is exactly what the tutorial says as well.

Creating your first screen

Did you note how the application is looking for the files inside of the dist directory? I missed that the first time through. It’s because of the System.config directive in the index.html file. You can change it to whatever you want. I’m going to leave it as dist and create my files in that directory. I cut and paste the files from the tutorial, so I’m not going to show them here.

One of the things I love about using Visual Studio is that the HTML file and the JS file are hooked together. Web Essentials has support for Aurelia. I’m not sure if that support is what I’m seeing here, but it’s nice to see.

Check out the Javascript console:

blog-0708-4

Obviously, I need to compile the ES6 code I am writing into JavaScript. Alternatively, I could write TypeScript code and get the same effect. Visual Studio automatically compiles a TypeScript file into JavaScript on save. To do this, I deleted the app.js file and created a new TypeScript file:

blog-0708-5

There will be a pop-up saying that you will need to configure compilation externally using Grunt or Gulp. Click on No and ignore it. Visual Studio compiles the code for you unless you are doing something special (which I may later on).

Cut and paste the same code into the app.ts file and save. The resulting app.js is found if you expand the app.ts node. It’s actually rather compact and looks like code I might have written myself.

Run the project. Revel in your “oh my god – it actually worked!” moment. I just got Aurelia working with TypeScript inside of Visual Studio.

Adding Navigation

Moving onto the navigation section. First step was to rename app.html and app.js to welcome.*. Visual Studio makes this really easy – just rename the html file by right-clicking on it and selecting Rename. Enter the new name (welcome – the extension stays the same) and all the linked files are renamed at the same time.

Once that was done, I created the new app.html and app.ts files as before. Side effect of the move to TypeScript is that the syntax highlighting now works as expected. Sometimes, small things are so impressive.

To get this loading properly, I need to install some more libraries. Specifically, this part relies on Bootstrap and Font Awesome, which you can install with the following commands:

jspm install "bootstrap"=github:twbs/bootstrap
jspm install "font-awesome"=npm:font-awesome
jspm install "css"=github:systemjs/plugin-css

That last one had an issue. It introduced an artifact in config.js. In the System.config map, there was a blank entry:

System.config({
  "map": {
    "": "github:systemjs/plugin-css@0.1.12",

This needs to be removed. The css plugin is configured further down in the map. This plugin allows you to import CSS using ES6 or TypeScript imports, like this:

import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

export class App {
    configureRouter(config, router) {
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' }
        ]);
        this.configureRouter = router;
    }
}

Side note – Visual Studio complains about a –module error inside the editor when editing a TS file with import/export. You can ignore that error. It’s a known issue since Web Essentials VS2013 Update 3 and just hasn’t been fixed in VS2015 yet.

Don’t forget to add the Font Awesome stylesheet to the index.html file at this point:

<!DOCTYPE html>
<html>
<head>
    <title>Aurelia-2</title>
    <link rel="stylesheet" href="jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css" />
    <link rel="stylesheet" href="style/site.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>

I also added a copy of the stylesheet from the Aurelia tutorial. I could create this as a less file. The idea, after all, is to get productive as a developer. However, this would require me to get a build system in the way. The provided CSS does give a great reason why pre-processors and post-processors are around, however. Take a look at all the purple squiggly lines where the author has not handled all the necessary codes. If I were compiling this from less, using mixins and transforming this with auto-prefixer, this would be handled for me.

Adding A Second Page

The tutorial now goes on to add a second page. It also slightly confused me by telling me that since I was using TypeScript, I could use a special decorator. Let’s start at the central app.ts file:

import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

export class App {
    configureRouter(config, router) {
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' },
            { route: 'flickr', name: 'flickr', moduleId: './flickr', nav: true }
        ]);
        this.configureRouter = router;
    }
}

The route that I added (highlighted) is the only thing I changed in this file. The route map is a central concept within an MVC application. If you think back to the ASP.NET MVC6 configuration stuff you put inside the Startup.cs file, it generally contains a route map as well.

Start by installing the http client library:

jspm install aurelia-http-client

Running the resulting code brings me to a problem. Where are the navigation links? I have the code for producing the navigation links – it’s in the app.html file:

<template>
    <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">
                <i class="fa fa-home"></i>
                <span>${router.title}</span>
            </a>
        </div>

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
                    <a href.bind="row.href">${row.title}</a>
                </li>
            </ul>

            <ul class="nav navbar-nav navbar-right">
                <li class="loader" if.bind="router.isNavigating">
                    <i class="fa fa-spinner fa-spin fa-2x"></i>
                </li>
            </ul>
        </div>
    </nav>

    <div class="page-host">
        <router-view></router-view>
    </div>
</template>

The problem was in my app.ts file:

import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

export class App {
    configureRouter(config, router) {
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' },
            { route: 'flickr', name: 'flickr', moduleId: './flickr', nav: true }
        ]);
        this.configureRouter = router;
    }
}

The line should read this.router = router. The Visual Studio auto-correct had bitten me. In fact, the Visual Studio auto-correct bites me often, so be aware of that fact. Once I re-ran (and cleared the cache) with the new file, I got a Welcome, but no Flickr. The link was there, but because the title didn’t exist in the route, there was no text to display. To fix this, I change the app.ts again:

import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

export class App {
    configureRouter(config, router) {
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' },
            { route: 'flickr', name: 'flickr', moduleId: './flickr', nav: true, title: 'Flickr' }
        ]);
        this.router = router;
    }
}

I added a title field to the route. Note to self: If you set nav to true (or a number), also set a title.

This created a second issue. When I clicked on the Flickr link, things worked fine. When I clicked away (say, back to the Welcome page), I got a warning that I was navigating away from the page and asking if I actually wanted to do that – Are you sure you want to do that?

That message is from the canDeactivate() method within the Flickr class. The confirm method is similar to alert(). You could, of course, just return true here…

    canDeactivate() {
        return true;
    }

Or I can do something more notable. For example, let’s say I don’t mind browsing away if nothing is loading, but I want to ensure that I get a confirmation if things are still loading:

@inject(HttpClient)
export class Flickr {
    heading = 'Flickr';
    loading = false;
    images = [];
    url = 'http://api.flickr.com/services/feeds/photos_public.gne?tags=rainier&tagmode=any&format=json';

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

    activate() {
        this.loading = true;
        return this.http.jsonp(this.url).then(response => {
            this.images = response.content.items;
            this.loading = false;
        });
    }

    canDeactivate() {
        if (this.loading) {
            return confirm("Still loading - are you sure?");
        }
        return true;
    }
}

In this case, I keep a boolean around that tells me if something is loading. This is much better code for a real system, but I appreciate the introduction that the tutorial provided.

Creating a Custom Element

Anyone who has been reading this blog for a while knows I love Web Components. Fun fact – you can use ANY web component you like within Aurelia. (Side thought – I wonder if you can use React components in Aurelia? Another page for investigation). The tutorial abstracts the navigation bar into it’s own component.

to keep the components separate from the pages, I decided to create a new directory called “components” right inside wwwroot. Here is the new app.html:

<template>
    <require from="../components/nav-bar"></require>

    <nav-bar router.bind="router"></nav-bar>

    <div class="page-host">
        <router-view></router-view>
    </div>
</template>

The nav-bar.ts and nav-bar.html are direct copies of the files included in the template, just in the components directory instead.

Final Reorganization

Ok – final thing. I disliked having the pages in the same page as the router and the directory called dist. To fix this, I moved the app.ts and app.html files up to the top level (the same level as index.html). Here is my new index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Aurelia-2</title>
    <link rel="stylesheet" href="jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css" />
    <link rel="stylesheet" href="style/site.css" />
</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 is actually less code. Here is my new app.ts:

import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

export class App {
    configureRouter(config, router) {
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'welcome'], name: 'welcome', moduleId: './pages/welcome', nav: true, title: 'Welcome' },
            { route: 'flickr', name: 'flickr', moduleId: './pages/flickr', nav: true, title: 'Flickr' }
        ]);
        this.router = router;
    }
}

The change is in the moduleId, which is the path to the actual page.

Wrapping Up

This was much easier than the equivalent methodologies I used the first time round. Aurelia has come a long way in the intervening months.

Also, watch what Visual Studio does for you. Sometimes it will introduce errors (as I saw when doing this tutorial).

In the next article, I’m going to look at Authentication and how that affects a single-page application. In the mean time, you can check out the code for this part at my GitHub Repository.