Creating Multipage SPA’s with AngularJs and TypeScript

Ok in the last post we created a really simple “Hello World” application using AngularJS and TypeScript. This was probably the simplest of apps we could write but it did give us a chance to introduce a number of key concepts. I’ll list them here.

• AngularJs bootstrapping
• AngularJs data binding
• Setting up Webstorm for TypeScript dev
• Building a basic controller.

So that’s great but we want to go further than this. Often when we build applications in technologies such as Java or .Net we use a number of different libraries/apis. In the last post we pulled in one such library (AngularJS) but we didn’t actually need to use any of its api and instead relied on the declarative features. Now we will go beyond that and dip our toes into parts of the api.

What’s a SPA?

The buzz word this year is SPA’s or Single Page Applications. If you haven’t come across this term yet don’t worry as it’s still quite new and will become more prevalent over the course of the the next few years. First let’s clear up some confusion about what SPA means as I think it can lead to some confusion. Although your app is called an SPA and does run inside of a single html page, that doesn’t mean that you’re limited to a very simplistic UI or that everything must be shown on screen at the same point.

You should look as SPA’s as the same way you would look at WinForms or WPF application. The “Single Page” part of the acronym is really just your application container or host. When you come to build your app you will see that you can actually have 100’s of pages/screens in a SPA. In the same way you add a new page/form to a WPF application running on a user’s desktop, you will see we do exactly the same for our SPA apps.

Create our host page.

Before we get into the creation of multiple pages and routing in AngularJS let’s talk about the host page first. In traditional .net apps when you start them there are a number of things happening. Two of the most important however is the loading of your custom dependencies, normally dll’s you’re are using as well UI initialization where you may be loading a custom theme which will be applied to your app (app.xaml).

You normally load these things upfront because they are core to the application and are often required to be available before your application code starts running. The same is true for SPA’s therefore we need a host page which will do this work for us.  Lets take a look at a really simple host for our application.


<html ng-app="myApp">
<head>
    <title>Multi-page SPA - Typescript and Angular</title>
    <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js" type="text/javascript"></script>
    <script src="app.js" type="text/javascript"></script>
</head>

<body>

<div>
   <div ng-view></div>
</div>

</body>
</html>

If we take a moment to dissect this html there are only a couple of things worth mentioning. First we are loading 3 dependencies into our app.

Bootstrap – An awesome CSS start kit from twitter which gives you out of the box theming for you application
• The AngularJS library
• app.js which is our application code.

The second is that we have an new AngularJs directive ng-view.

What’s ng-view?

If you look at the body of our page you will notice I’ve introduced a directive from the AngularJs framework called ng-view. The ng-view directive in angular is incredibly powerful. It works in conjunction with AngularJs’s routing provider and is used to inject fragments of HTML into the DOM where that directive has been applied. The best way I can think to describe this is to compare it to XAML construct, the ng-view directive being applied to the div essentially turns the div into a XAML’s content presenter control. So that’s our host page setup, I don’t need anything else for this demo in the host so lets move forward.

Enabling intellisense and type checking for AngularJs

As I mentioned the ng-view directive works in with Angulars routing provider in order to display content, but as it stands our application has no routes and we need to configure these for this to work. It’s now that we get a chance to play with the AngularJs api. As we are using TypeScript we can take advantage of one of its key selling points, the ability to pull in type definitions for any JavaScript library and provide us with full intellisense and type checking. These definitions are called ambient declarations and work similar to how c++ header files work.  There is actually a site which has many of the most popular definitions already defined for us, its called DefinitelyTyped 

I went to this site and downloaded the angular.d.ts file as well as jQuery and added it to my Webstorm project. NOTE: As d.ts files are purely definitions they do not get compiled into JavaScript which is why I do not need to reference the files in my host page. They are  there to support the tooling. 

For the sake of simplicity I only want to have a couple of pages in my app as I think this illustrates the concept well enough. I would like page1, page2 and then when the user attempts to navigate to a page that doesn’t exist I’d like to show them a 404. So I have just added these to my project under a folder called pages. You can see what my final project structure looks like here.

multi-spa-1

Defining our routes

We now have our app setup with all the files we need, the last thing we want to do is define our routes and enforce the behavior I’ve specified. Let’s jump to the app.ts file and write some code.


/// <reference path="includes/ts/angular.d.ts"/>

var app = angular.module("myApp", []);

//Set up our site routes
app.config(($routeProvider) => {
    $routeProvider
        .when('/page1',  <ng.Route>{ templateUrl: 'pages/page1.html' })
        .when('/page2', <ng.Route>{ templateUrl: 'pages/page2.html' })
        .when('/404',  <ng.Route>{ templateUrl: 'pages/404.html' })
        .otherwise( <ng.Route>{ redirectTo: '/404' });
})

Ok breaking this code down, you can see the very first thing we do is to instruct the TS compiler and tooling that we want to import all the type definitions for AngularJs. This is what gives us full intellisense and type checking around the api.


/// <reference path="includes/ts/angular.d.ts"/>

multi-spa-2
Next we define a new angular module. Angular modules are really a way for you to break your application into composable chunks. We will cover this in more depth in a future post, for now just note that the name of the module matches the one we defined in our html host. We will be able to configure this module in the next step.


var app = angular.module("myApp", []);

The final piece in the puzzle is the definition of the routes. Using the config object which hangs of our module we can inject a $routeProvider. Don’t worry about how this gets injected just yet as we will cover AngularJs’s injection in a future post, just trust in the fact that you will be given a $routeProvider and that it’s this object we will use to configure our routes.


//Set up our site routes
app.config(($routeProvider) => {
    $routeProvider
        .when('/page1',  <ng.Route>{ templateUrl: 'pages/page1.html' })
        .when('/page2', <ng.Route>{ templateUrl: 'pages/page2.html' })
        .when('/404',  <ng.Route>{ templateUrl: 'pages/404.html' })
        .otherwise( <ng.Route>{ redirectTo: '/404' });
})

In the configuration you can see it uses a fluent API to define routes, specifically it uses “when” to define the rules of route and where it should go. We setup three routes for our page1, 2 and then our 404. The final thing we do is use the “otherwise” method define the fall back when no route has been found that matches the rules. In our case the 404 page.
At that point we are good to go, if we run the app and try different urls like default.html#/page1 or default.html#/unknown we can see the route provider is doing its job and taking us where we want to go. Now behind the scenes AngularJs is doing a lot of the heavy lifting for us, firstly it is monitoring the location of the url and when that changes it checking the list of routes that have been defined in order to see if we there is a matching rule, then if it finds a matching rule is downloads the template from the server (no they are not eager loaded) then once downloaded the ng-view attribute that we had in our host is injecting the html fragment into the DOM. There is tonne of stuff I’m leaving out here but that’s the gist of it.

Summary

You can now see in a very simple demo how we can build multipage SPA’s. The ability to dynamically load a screen into your SPA gives you a very powerful composition model, as we explore more about the AngularJs api you will see many examples of how this and other types of composition can help us build very complex ui. You can grab the code for this demo here.

Advertisements

Tagged: , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: