Getting Started with Angular UI Router

Video Tutorial

Hi everyone! I’ve been in Sweden for the last week or so, so I was a bit delayed in posting here, but it’s been great fun so far! I did a few lessons last week and really liked using the Angular UI Router. I’ve just begun learning about how it works, so I thought it would be nice to do a tutorial on it to solidify what I’ve learned. I ended up running into a couple of snags along the way. One of them was annoyingly simple–a missing script tag. The other one was actually a good illustration of the nested routes provided by Angular UI router and how they work when combining a parent and child state.

In my example, if you have a parent state called ‘movie’ that has the url: ‘/movie’, and then you add the child state ‘movie.cast’ that has the url: ‘/movie/cast’, you actually would end up with the url ‘/movie/movie/cast’! So what you need to remember when creating nested states is that the urls are concatenated. For example, you only need ‘/cast’ for the url of the ‘movie.cast’ state. That would result in the following actual url: ‘#/movie/cast’.

I’ve created a github repository that accompanies this video screencast series where you can follow along with me. I will be adding some nested states to a basic application that shows information about a movie, its cast, description, trailer and upcoming showtimes. The application demonstrates how to use Angular UI router to create tab based navigation to show the showtimes, cast, description and trailer for a movie. I’ve use the Legend of Tarzan movie as an example (which I really enjoyed by the way!).

Part 1: Setup, requirements and an overview of project content

If you want to follow along with the tutorial step by step, there are 3 things you’ll want to make sure that you complete.

  1. Have a github account
  2. Install the Visual Studio Code text editor so you can use the code snippets I’m using in this tutorial.
  3. Make sure you have node.js installed.

Once these steps are complete, this first video covers the following topics:

  • my favorite features of Visual Studio Code: we go through how git integration and the attached terminal work in the context of a project.
  • We’ll fork and clone the project from github and run npm install to pull in dependencies.
  • we’ll run the local server and make sure it’s working
  • we’ll pull in some javascript libraries (angular, angular-ui-router and angular-mocks) that we’ll need when we set up our test suite in the third video
  • we’ll take a look at the views that will make up the content of the tabs once we set up UI-router.

After you’re finished following along with this video, you’ll be ready to get started with the next video. In the next video, we’ll be adding the tab functionality by injecting Angular UI router into the project. Note that if you ever want to peek ahead to see where the code is going, you can take a look at the solution branch when viewing the code on github.

Part 2: Implementing Angular UI-Router to add nested States and Views to our application

 

This video covers the following:

  • adding the Visual Studio Code Snippets used in this video and the following video to your installation of Visual Studio Code.
  • using the snippets to bootstrap a simple AngularJS application and inject Angular UI Router as a dependency, creating 3 child states nested within 1 parent state for tabbed design with unique URLs for each tab. (I ran into an error at around 7:00 in, paused to solve it, and resumed at around 8:00 to explain the solution)
    The parent state is ‘movie’, the child, nested states, are ‘movie.cast’, ‘movie.description’, and ‘movie.trailer’
  • using the ui-view directive to instruct UI router where to insert the content corresponding to our states.
  • using the ui-view directive within the template of the parent view (movie.html) to allow space for the child view templates to be inserted into the parent template.
  • using Angular UI Router’s ui-sref attribute on links (instead of href) to create state based navigation that allows for updating URLs without having to track down a bunch of links (because the links are to the names of the states and the urls are determined by the url attribute of the state object)
  • using the tachyons css library to add some basic styles to the tabs.

Visual Studio Code Snippets

To add the javascript code snippets to your installation of Video Studio Code, you’ll want to do the following:
– visit the javascript.json file in my github repository, select all and copy.
– go back to Visual Studio Code, you’ll want to open Code > Preferences > User Snippets, type in Javascript and hit Enter. Then you can paste in the contents of the clipboard into your javascript.json file.

You should now have access to all of the snippets used in the tutorial series on Angular UI Router. Feel free to check out the Visual Studio Code website for more information on how to add your own Visual Studio Code snippets.

Adding States to your application using Angular UI Router

UI router makes it pretty easy to create URLs that correspond to each of the active tab states (/movie/cast will appear in your URL bar when looking at information about the cast). UI router works by injecting the $stateProvider object into the config function that’s called when your Angular app is bootstrapped:

// js/app.js
angular
.module('app', ['ui.router'])
.config(function($stateProvider) {
$stateProvider
.state('movie', {
url: '/movie',
templateUrl: 'views/movie.html'
})
.state('movie.cast', {
url: '/cast',
templateUrl: 'views/movie-cast.html'
})
...

Adding the ui-view directive to our parent template

Then, as you might have guessed, you’ll have a folder for views containing the content that corresponds to each of these states/nested states. The way that Angular UI Router binds itself to the view is by using the ui-view directive. When we insert the ui-view directive into an element, the contents of the template that corresponds to the currently active state will be displayed within the element that has the directive.

To make this a little clearer, let’s take a look at the template at views/movie.html so we can see where that ui-view directive is applied:

<!-- views/movie.html --> 


<nav>


<ul class="list">


<li class="dib-ns"><a class="no-underline pa2 pl0-ns" href="" ui-sref="movie">Showtimes</a></li>




<li class="dib-ns"><a class="no-underline pa2" href="" ui-sref="movie.cast">Cast</a></li>




<li class="dib-ns"><a class="no-underline pa2" href="" ui-sref="movie.description">Description</a></li>




<li class="dib-ns"><a class="no-underline pa2" href="" ui-sref="movie.trailer">Trailer</a></li>


  </ul>


</nav>




<div ui-view class="ph4-ns ph2">


<div class="w-50 pa2 fl">
    <img src="../img/legend-of-tarzan-poster.jpg" alt="The Legend of Tarzan Movie Poster" class="mw-100" />
  </div>




<div class="w-50 pa2 fl showtimes">


The Legend of Tarzan is playing at:



  Cinemark Playa Vista and XD



    11:10AM    2:00    4:40    7:50    10:40

    ...

In this example, we apply the ui-view directive to the div in the movie state (the parent state) that has the showtime information. That way, when we click on one of the navigation links that will take us to a nested state, the content of the div with the ui-view directive will be replaced with the content of the template for the new active (nested) state.

Using Angular UI Router’s ui-sref attribute

You’ll notice that those navigation links use the ui-sref instead of the href attribute. This allows us to enter the name of the state (as a state reference to ui-router) to create the link. This lets Angular UI router know which template and URL and controller to display within the element containing the ui-view directive given the current state of your application.

By using ui-sref in this manner, we can change the urls of all of our links in one place. We do this by going into the app.js file where we have our states defined. Inside one of our state objects, we can change the value of the url property.

// js/app.js
angular
  .module('app', ['ui.router'])
  .config(function($stateProvider) {
    $stateProvider
      .state('movie', {
        url: '/legend-of-tarzan',
        templateUrl: 'views/movie.html'
      })

Now all of our links to the ‘movie’ state will point to ‘#/legend-of-tarzan’ and when we click on the Cast tab, everything still works and the url will be ‘#/legend-of-tarzan/cast’. Using Angular UI Router’s ui-sref attribute on anchor links is recommended. Doing so allows us to make changes to our url structure in a much simpler way. When we change the value of the url property on a state object, all of our links that involve that state will update automatically.

Part 3: Adding Tests for your Angular Ui Router states

This video covers:

  • setting up karma configuration to load libraries and spec files for tests.
  • using the snippets provided in the repository to set up tests for your routes.
  • how to run your tests and make sure your routes are working properly.

Setting up Karma Configuration

The tests we’re writing will be run by the Karma test runner. First, we have to configure Karma for our project. We need to create a file called karma.conf.js in the root of our project. I’ve highlighted the important lines here below:

// karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '.',
    frameworks: ['jasmine'],
    files: [
      'testing/angular.min.js',
      'testing/angular-ui-router.js',
      'testing/angular-mocks.js',
      'js/**/*.js',
      'testing/*.spec.js'
    ],
    exclude: [],
    plugins: [
      require("karma-chrome-launcher"),
      require("karma-jasmine"),
      require("karma-spec-reporter")
    ],
    preprocessors: {},
    reporters: ['spec'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    browsers: ['Chrome'],
    singleRun: true,
    concurrency: Infinity
  });
}

In the files property of our configuration object, we’re listing an array of files that will be loaded by Karma. The angular.min.js, angular-ui-router.js and angular-mock.js will be the first files loaded. They are necessary for our spec to run so they must be at the beginning of the array. Next, we’re loading all of the .js files in the js folder (these files are our app). And finally, after we’ve loaded all of the libraries required by the project and all of our code, we load all of our test files that will run against our code.

The other highlighted lines ensure that Karma is able to launch chrome and use jasmine and spec reporter. Jasmine is a Javascript library that creates the syntax that we use to write our tests.

Using Visual Studio Code Snippets to write our tests

To write our tests, we’ll create a file in our testing folder called ‘routes.spec.js’. It’s important that we name it ending with ‘.spec.js’ so that karma will know to load it.

To set up our test file in routes.spec.js, we’ll type ‘ngroutestest’ and hit the ‘tab’ key. Type in ‘routes’ as a heading for our tests and this will expand to the following:

describe('routes', function() {
  var $state;
    
  beforeEach(module('app'));
    
  beforeEach(inject(function($injector) {
    $state = $injector.get('$state');
  }));
});

This code will allow us to test our applications routes by giving us access to the $state object. Once we inject the $state object into each of our tests, we’ll be able to make assertions against our states. To create a group of tests for each of our states, we can use another snippet.

Let’s type in ‘routetest’ and hit ‘tab’ to expand. After that we’ll see the following:

describe('', function() {
  var $state;

  beforeEach(module('app'));

  beforeEach(inject(function($injector) {
    $state = $injector.get('$state');
  }));

  describe('', function() {
    var state;
  
    it('should have the correct URL', function() {
      state = $state.get('');
      expect(state.url).toEqual('');
    });
  
    it('should use the correct template', function() {
      expect(state.templateUrl).toEqual('');
    });
  
    it('should call the correct controller', function() {
      expect(state.controller).toEqual('');
    });
  });
});

Take a look at your js/app.js and fill in the proper values for each state and you’ll have test coverage for all of your routes! For this demo, we’ve only used urls and templates, no controllers. As a result, our final test file looks like this:

describe('routes', function() {
  var $state;

  beforeEach(module('app'));

  beforeEach(inject(function($injector) {
    $state = $injector.get('$state');
  }));

  describe('movie', function() {
    var state;
  
    it('should have the correct URL', function() {
      state = $state.get('movie');
      expect(state.url).toEqual('/movie');
    });
  
    it('should use the correct template', function() {
      expect(state.templateUrl).toEqual('views/movie.html');
    });
  
  });

  describe('movie.cast', function() {
    var state;
  
    it('should have the correct URL', function() {
      state = $state.get('movie.cast');
      expect(state.url).toEqual('/cast');
    });
  
    it('should use the correct template', function() {
      expect(state.templateUrl).toEqual('views/movie-cast.html');
    });
  
  });

  describe('movie.description', function() {
    var state;
  
    it('should have the correct URL', function() {
      state = $state.get('movie.description');
      expect(state.url).toEqual('/description');
    });
  
    it('should use the correct template', function() {
      expect(state.templateUrl).toEqual('views/movie-description.html');
    });
  
  });

  describe('movie.trailer', function() {
    var state;
  
    it('should have the correct URL', function() {
      state = $state.get('movie.trailer');
      expect(state.url).toEqual('/trailer');
    });
  
    it('should use the correct template', function() {
      expect(state.templateUrl).toEqual('views/movie-trailer.html');
    });
  
  });
});

Running our tests

Finally, the last step is to run our tests. To do that, you’ll want to open the attached terminal (using ctrl + `), type:
karma start
and hit the return key. Consequently, Karma will load your project into chrome, recreate the states specified in your tests and make sure that the correct URLs and templates were used.

Takeaways

  • Angular UI Router is a very powerful tool.
  • You can use it to implement lots of great dynamic features that make use of quick navigation without losing linkable URLs.
  • Also, You can also update urls for multiple links very easily by using Angular UI Router’s ui-sref attribute on your links to reference different states.
  • Using Code snippets provided by a text editor like Visual Studio Code will allow you to speed up your development process dramatically

Leave a Reply

Your email address will not be published. Required fields are marked *