AngularJS UI-Router – Example
This is the second post of a series of 3, presenting AngularJS UI-Router library:
Example
After having seen an introduction to the library and the main components which compose the AngularJS UI-Router, in this post we will develop a simple example to see how we can make use of this library to create a simple application with a powerful routing mechanism and how to use multiple views in routes.
In this example, we are going to create a simple application that shows a list of posts and the authors of a website.
Setup
In order to get our application started, we need to create and fill some files:
- index.html // will contain the HTML code template - application.js // AngularJS code - page-posts.html // Posts page - page-authors.html // Authors page - page-posts-list-html // Posts list page - view-popular-authors.html // Popular authors view
After creating this structure of files, we are ready to start writing the code.
Index.html
<!-- File: index.html --> <!DOCTYPE html> <html> <head> <!-- JS dependencies --> <!-- AngularJS library --> <script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9hamF4Lmdvb2dsZWFwaXMuY29tL2FqYXgvlibs.angularjs/1.3.15/angularjs.min.js></script> <!-- AngularJS UI-Router --> <script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script> <!-- Our application --> <script src="application.js"></script> <!-- CSS dependencies --> <link rel="stylesheet" href="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS8=bootstrap/3.3.5/css/bootstrap.min.css"> <title>WebCodeGeeks AngularJS UI-Router Example Application</title> </head> <!-- AngularJS Application --> <body ng-app="wcgUiRouterApplication"> <!-- Simple Navigation Bar --> <nav class="navbar navbar-inverse" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" ui-sref="#">WebCodeGeeks AngularJS UI-Router Example</a> </div> <ul class="nav navbar-nav"> <li><a ui-sref="posts">Posts</a></li> <li><a ui-sref="authors">Authors</a></li> </ul> </nav> <div class="container"> <!-- ui-view attribute is the indicator where to inject the views managed by AngularJS UI-Router --> <div ui-view></div> </div> </body> </html>
This is our simple index file for our application. As it can be seen, we are declaring our JS (AngularJS, AngularJS UI-Router, application) and CSS (Bootstrap for styling) dependencies needed. AngularJS UI-Router library has to be explicitly declared because it is an external library not shipped with the AngularJS core library.
The ui-view attribute indicates the place where the AngularJS UI-Router library will load the state templates depending on the state active on each moment. Lastly, the ui-sref attribute is used when we want to create links to states. In this case, we are creating to links, posts and authors, in order to navigate to those states when clicked. The link will be generated when AngularJS UI-Router library processes those attributes and they will point to that specific state in the application.
In a similar way than AngularJS ngRoute, the states and views are defined in a JS file (in our case, the application.js file).
application.js
// File: application.js var wcgUiRouterApplication = angular.module('wcgUiRouterApplication', ['ui.router']); wcgUiRouterApplication.config(function($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/posts'); $stateProvider .state('posts', { // Posts state. This state will contain nested views url: '/posts', templateUrl: 'page-posts.html' }) .state('authors', { // Authors state. This state will contain multiple views url: '/authors', views: {} // we will fill this later }); });
After we have created this file, we already have our application configuration ready to start filling the rest of the files. As you can see, we have created the wcgUiRouterApplication which we have tied to the ng-app attribute in the body tag in the index.html file.
The $stateProvider provider of AngularJS allows to configure the states we are going to use in our application. In our case, we use the state method of the provider to create the configuration for our two states, posts and authors. Regarding the posts state we have configured the page-posts.html as template to be used when rendering that state.
So our next step is to create the necessary code for that page.
In order to show the potential of AngularJS UI-Router library, in the posts page we are going to use Nested Views and in the authors page we will make use of Multiple Views.
<!-- page-posts.html --> <div class="jumbotron text-center"> <h1>Posts page</h1> <p>This page shows a list of posts and it is intended to show the use of Nested Views.</p> <a ui-sref=".list" class="btn btn-primary">List</a> <a ui-sref=".info" class="btn btn-info">Information</a> </div> <div ui-view></div>
In the page-posts.html template, the root of our application as defined in the application.js file, we have to links (list and info) which will link to the two nested (child) states (views) of the posts state. The ui-sref attribute acts like the href attribute of HTML links but it tries to link to states. In this case, we will link to list and info nested states.
In order to define nested states in AngularJS UI-Router, we use the “dot nomenclature“. For example, .list means that .list is child state of the current state (posts), so the state name in the JS configuration file should be “posts.list”. The same applies for the information child state. Of course, we can use the full state identifier instead of the short one (e.g posts.list)
Once the states have been properly defined and we navigate to them (through links with ui-sref attributed or through URL) they will be injected into the div containing the ui-view attribute. This approach is the same as the one we saw in the index.html page, but in this case we inject a portion of the content.
The use of ui-view attributes is the basis of AngularJS UI-Router because it will use the element where ui-view attribute is placed, as placeholder for the template it has to inject depending on the state.
Let’s create the new nested states (child states) in the application.js file.
// File: application.js ... $stateProvider .state('posts', { // Posts state. This state will contain nested views url: '/posts', templateUrl: 'page-posts.html' }) .state('posts.list', { // Posts list state. This state is child of posts state url: '/list', templateUrl: 'page-posts-list.html', controller: ['$scope', function($scope) { $scope.posts = [ {id: 1, name: "WCG Post 1"}, {id: 2, name: "WCG Post 2"}, {id: 3, name: "WCG Post 3"}, {id: 4, name: "WCG Post 4"}, {id: 5, name: "WCG Post 5"}, ] }] }) .state('posts.info', { // Posts info state. This state is child of posts state url: '/info', template: 'Posts information. We are using directly a string template instead of a url linking to a template.' }); ...
We have defined the two nested states we used in the posts page. The next thing to do is to fill the page-posts-list.html template. As you can see, we have initialized the scope with a list of posts to be shown.
In a real scenario, we should use another approach, like an AJAX call to request the list of posts to an API and initialize the scope accordingly using the obtained response.
<!-- page-posts-list.html --> <ul> <li ng-repeat="post in posts"> {{post.id}} - {{post.name}} </li> </ul>
We are done. When we click List button in the posts page, a list of predefined posts will be injected in the ui-view of posts page. Likewise, if we click on Information button, the String template defined for the info state will be injected.
Now, the next step is to finish our Authors page. As we stated before, we will show in this state the use of multiple views. Unlike the nested states which depend on the parent state and have an URL associated to them (also built from the URL defined by the parent), the Multiple Views allow to inject different blocks of information to a template and enabling each block of information be managed by the same or different controller.
Having the ability to use Multiple Views in an application is very useful. For example, many templates have a sidebar where different kind of information is shown: Popular posts, Tweets, Recommended posts, etc. By using the Multiple Views we can split each block of information and inject them in our template as needed. Each multiple view can have its own controller and template file, so the application stays clean and tidy.
Furthermore, it also allows to keep our application modular because this lets us to reuse data in different templates.
The next step is to complete the Authors page. In this page we will use the Multiple Views approach, creating two columns and each column having its own data. One column will show the popular authors and the other will show the recent authors. Let’s complete first the templates and then we will modify the application.js file to add the needed configuration.
<!-- page-authors.html --> <div class="jumbotron text-center"> <h1>Authors page</h1> <p>This page shows a list of popular and recent authors and it is intended to show the use of Multiple Views.</p> </div> <div class="row"> <!-- Popular authors named view --> <div class="col-sm-6"> <div ui-view="popular"></div> </div> <!-- Recent authors named view --> <div class="col-sm-6"> <div ui-view="recent"></div> </div> </div>
Here we can see the use of Multiple Views. For that, we need to give a name to the ui-view attribute which AngularJS UI-Router framework will use to inject the appropriate view. In our case, we have given popular and recent as view names.
Now, let’s modify the application.js file to add the necessary configuration for the multiple views to work.
// File: application.js ... $stateProvider .state('authors', { // Authors state. This state will contain multiple views url: '/authors', views: { // Main template. It will be placed in the ui-view of the index.html file when /authors url is visited (relatively named) '': { templateUrl: 'page-authors.html' }, // popular child view. Absolutely named. It will be injected in the popular ui-view of authors state 'popular@authors': { templateUrl: 'view-popular-authors.html', controller: ['$scope', function($scope) { $scope.authors = [ {name: 'John', surname: 'Benneth'}, {name: 'Anthony', surname: 'Horner'}, {name: 'James', surname: 'Blanch'}, {name: 'Harrison', surname: 'Williams'}, ]; }] }, // recent child view. Absolutely named. It will be injected in the recent ui-view of authors state 'recent@authors': { template: 'No recent authors since last month' } } }); ...
In order to finish our example, let’s fill the view-popular-authors.html template
<!-- view-popular-authors.html --> <ul> <li ng-repeat="author in authors"> {{author.name}} {{author.surname}} </li> </ul>
Conclusion
This is an example on how to use the AngularJS UI-Router to create a simple application using URL routing and some features we can use when using this amazing library. As we have seen, using states and views we can create modular applications easily keeping a simple and clean structure of files. Since the configuration is very simple, it can be extended as needed without affecting other parts of the application.
There are many more features, for example transitioning to a state only if some preconditions have been fulfilled, like the getting the response from an AJAX call to initialize the scope before moving to the requested state.
Code
A JSFiddle has been created with the code of this example. You can find it here .
Note: Due to some JSFiddle limitations, the JSFiddle example contains all the HTML templates for states and views as String (instead of different HTML files)
References
AngularJS UI-Router wiki
AngularJS UI-Router Documentation