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.
And that was constructed from the DDS JSON data as we saw before
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
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