Angular.js in XPages #5 – Routing

In this article I will show how angular using routing to create a bookmarkable URL string and how that fits together with additional controllers. This will start us on the path to creating our basic CRUD application. This article is based on the Routing and Multiple views tutorial 

Routers

Way back when the world was young (in internet terms a couple of years ago), when single page apps were all the rage, it was noticed that there was a significant drawback to only having one URL index.html; it was useless as a bookmark. Even worse it was impractical to send a link to a page within the application, and that made for some serious limitations.

Routing was created as a manner to not only provide a URL which could be referenced within the application, but it also helped to separate the code into discrete functional areas within the application. This route, this controller, this model and so on.

In Angular –“Providers are objects that provide (create) instances of services and expose configuration APIs that can be used to control the creation and runtime behavior of a service. In case of the $route service, the $routeProvider exposes APIs that allow you to define routes for your application.”

So we add the angular-route.js file which was in the original download to our database in the angularjs folder

app.js

Beginning our path towards true code separation we are going to create a new app.js file which will contain the architecture for the application. Our app.js file will contain the following:

  • The angular module and the dependencies for that module (previously we had a module with no dependencies)
  • The route configuration for the new application

In this way we are defining what our application needs to run (as an Angular app) and the route mapping (as we will see) for “this URL – this Template”

Because we are moving the module code up to the app.js we also need to create a ngView for the app rather than the ng-controller like we had before.

This will mean:

  • a change to the container DIV within index.html
  • moving the HTML to a template and not in the index.html itself

Overall we are not going to change that much code, we are just going to move it around to make more sense from an application perspective rather than a demo.

/**
 * Created by mroden on 5/4/2014.
 */

/* App Module */

var personApp = angular.module('personApp', [

    'ngRoute',
    'peopleControllers'
]);

personApp.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/people', {
                templateUrl: 'partials/people-list.html',
                controller: 'PeopleListCtrl'
            }).
            when('/person/:docId', {
                templateUrl: 'partials/person.html',
                controller: 'PersonDetailCtrl'
            }).
            otherwise({
                redirectTo: '/people'
            });
    }]);

You can see from the code above that we are going to create two new files – people-list.html and person.html. These are the two templates we are going to “route” to in this part of the application

 people-list.html

The people list template is replacing the HTML code we had before in the index.html. This time though instead of copying the mustache layout as we did before we are actually going to go for a simple table. In an analogous manner to using a repeat control in XPages we create the shell of the table and use the rows as the repeating element wrapper.

<table class="table table-striped">
    <thead>
    <tr>
        <th>First Name</th><th>Last Name</th><th>Zip</th><th></th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="person in people">
        <td>{{person.firstname}}</td>
        <td>{{person.lastname}}</td>
        <td>{{person.zip}}</td>
        <td><a class="btn btn-info" href="#/person/{{person['@unid']}}">Edit</a></td>
    </tr>

    </tbody>
</table>

If you look closely at the Edit button code here you will see that we are manually creating a route map for the person. The {{person[‘@unid’]}} template pointer is going to be filled with the JSON Object nth value and the “@UNID” parameter therein. Remember that @ symbols are classed as parameters and not real key pair values within JSON (*again dumb dumb idea IBM*). So in this case we cannot refer to it as person.@unid that would fail.

Looking at the HTML generated you will see that this creates a URL which may seem strangely familiar.
a1

And that was constructed from the DDS JSON data as we saw before
a2

person.html

In the person template we are going to display the 3 fields as if they were editable. The analogy here is between an XPage and the data binding to Document Source. In this case the source is the JSON string data retrieved from the DDS.

<legend>A Person</legend>

<div class="row" style="width: 50%">
    <div class="col-sm-4">
        <label>First Name</label><br/>
        <input class="form-control" name="firstname" type="text" value="{{person.firstname}}">
    </div>
    <div class="col-sm-4">
        <label>Last Name</label><br/>
        <input class="form-control" name="lastname" type="text" value="{{person.lastname}}">
    </div>
    <div class="col-sm-4">
        <label>Zip</label><br/>
        <input class="form-control" name="zip" type="text" value="{{person.zip}}">
    </div>
</div>
        <br/>
<div class="row" style="width: 50%">
    <div class="col-sm-12">
        <a class="btn btn-default" href="#/people">Back</a>
    </div>
</div>

<hr />

The controller

Within the controller.js file we now need to manage not only the download of data for the people, but also for the person who will be subsequently opened. In this case we need two controllers to handle the People and the Person. Both controllers access the DDS data, first for the view information and secondly for the document information.

If you look at PeopleListCtrl you will see only two dependencies ($scope and $http), whereas PersonDetailCtrl needs 3 ($scope, $http and $routeParams). This is because the People feed is always at the root of the application and therefore does not need Route information. The “Person” (as we saw above) is generated from the URL #/person/UNID.

Once the person and UNID is opened as a URL the controller then creates another ajax call to the DDS document service corresponding to the UNID in the route map. The JSON string downloaded is then turned into an object and $scope.people turned into a scoped object.

var peopleControllers = angular.module('peopleControllers', []);

peopleControllers.controller('PeopleListCtrl', ['$scope', '$http',
    function ($scope, $http) {
        $http.get('api/data/collections/name/byFirstName5Col?open&').success(function(data) { //DDS View feed
            $scope.people = data;
        });
    }]);

peopleControllers.controller('PersonDetailCtrl', ['$scope', '$routeParams', '$http',
    function($scope, $routeParams, $http) {
        $http.get('api/data/documents/unid/' + $routeParams.docId).success(function(data) { //DDS Document Feed
            $scope.person = data;
        });
        console.log($scope)
    }]);

 The result

We have created a simple demonstration of how we can using routing to create two different pages within an application


a3

a4

 

Conclusion

In this article we have seen the beginnings of a real application. The application is taking shape in an easy to manage , separated, set of code modules which reflects out MVC approach to the data, the HTML and the control thereof.

  • app.js
    • Provides the architecture for the application by defining the routing and the module dependencies
    • The routing map defines:
      • the URL to look for
      • the template to use to display the data
      • the controller to use to manage that data
  • controller.js
    • Contains the controllers which determine what happens based on the definitions as described in the route map
  • templates
    • Simple HTML templates which are used to display the bound data on the screen for the user

 

In the next article we will look at expanding this display application into a full CRUD capable application.
Updated
Fixed the routing code for a person thanks to comment from Angelo

12 thoughts on “Angular.js in XPages #5 – Routing

  1. Hi! I’m following this series of angularJS articles with great interest.. BUT!
    Being an angularJS illiterate I cannot figure what to put in the index.html in your example above…
    Please, can you update the article with that code also? Or put in a link to the file? Or reply with the file’s text?

    Thank you very much!
    Angelo

    • Hey Angelo.

      I will be publishing some code in the near future. Glad you are enjoying the articles 🙂

      • Thank for the quick reply, yeah, I’m definitely enjoying them! 😀

        Anyway, I fixed the index.html issue. But can’t make the edit button work 😦
        Still going forward 🙂

  2. Hey Marky!

    This series is awesome!
    I admit that I didn’t find the time to figure everything out yet due to two weeks of vacation and today starting another round of 6 weeks of heavy project workload. BUT: I will recap all the posts afterwards – I promise!

    It is very promising seeing your quick success using Angular in XPages apps so I am confident to try and succeed as well 😉

    Keep up the great work and the sharing of your knowledge – we all appreciate it!

    Cheers mate!

    • Thanks for the kinds words Oliver 🙂

      Really within only a few days (in hours spent) I had a CRUD app up and working.

      Toby (Samples) started using it in our current development and it makes life so simple and straight forward to develop.

      Lots more to learn and share I am sure 🙂

      • Wow, that’s great news! And great to read about Toby coming around using it in a “real” project as he started to work @ PSC. This seems to be a good start. Unfortunately PSC doesn’t have a European branch where someone like me could apply to 😉

  3. For those who don’t know what to put in index.html :

    Thank you Marky for all this awesome examples!

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