Angular in XPages #8 – Directives (did someone say plugins?)

In this article I will discuss Angular Directives and why they are near and dear to my heart

Directives

Directives in Angular are modular pieces of functionality which in some cases are very analogous to plugins in jQuery. According to the Angular documentation…

“At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS’s HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children.”

For more on the directive documentation look here – https://docs.angularjs.org/guide/directive

I have already shown and demonstrated some of the core Angular directives like ngApp and ngView. Those attributes within the HTML are the instructions to “Do Something Here”

<div ng-app="personApp">
    <div ng-view></div>
</div>

The on-last-repeat directive

Within my demo application I wanted to know when the table was finished loading. The problem is in Angular that there is no “I am finished with the template load” event. The reason is due to the asynchronous nature of the Angular code.

I wanted to load the table and then take an action on it. To do this I added a directive to the “repeat control” within my template for loading people.

The following example was completely plagiarized from this article – respect !

http://www.nodewiz.biz/angular-js-final-callback-after-ng-repeat/

You can see the on-last-repeat attribute within the first TR

    <tr ng-repeat="person in people"  on-last-repeat>
        <td>{{person.firstname}}</td>
        <td>{{person.lastname}}</td>
        <td class="zipCode">{{person.zip}}</td>
        <td class="user">{{person.username}}</td>
        <td><a class="btn btn-info" href="#/person/{{person['@unid']}}">Edit</a></td>
        <td><a class="btn btn-warning" href="#/person/{{person['@unid']}}/delete">Delete</a></td>
    </tr>

I then created a directive within my app.js to do something with the on-last-repeat

personApp.directive('onLastRepeat', function() {
    return function(scope, element, attrs) {
        if (scope.$last) setTimeout(function(){
            scope.$emit('onRepeatLast', element, attrs);
        }, 1);
    };
})

Within the code we are creating a custom “event” which can be listened for in another part of the application

  • If the last entry is triggeredhere
    then
  • $emit the onRepeatLast event within the application

Then in the PeopleListCtrl controller we set a listener –

  • $on the onRepeatLast event
  • Do something
peopleControllers.controller('PeopleListCtrl', ['$scope', '$http', 'peopleFactory',
    function ($scope, $http, peopleFactory) {
        $scope.$on('onRepeatLast', function(scope, element, attrs){
           alert('The table has loaded')
        });

a1

 

Well that is a little simple….

Yes it is but it serves to demonstrate the concept. The best part is that there are lots of people who are writing plug and play Directives for Angular – just like jQuery plugins. If I could be bothered I might even do some blog posts on useful Directives…..But is a good resource for looking at some available directives.

http://angular-js.in/

There are multiple directives for using jQuery plugins within an angular construct – this is very helpful for data binding. There are also multiple directives which are independent of jQuery and are stand along “Angular” plugins for want of a better term.

Working on my MWLUG2014 presentation

Just wanted to show a work in progress – screenshots of what I am working on for MWLUG2014. Part of the presentation is showing how to make your Angular applications portable.

My sample app will be the one I created as part of the Angular in XPages series.

I will walk through the code and demonstrate how I can take this stand alone application

a2

and insert it into this bootstrap demo dashboard application

a1

creating this fully functional component capability within the dashboard

angularinDashboard

 

With only 4 lines of HTML and zero XPages dependency.

Come to MWLUG2014 in Grand Rapids Michigan and find out more

PS

Make sure you plan to stick around to the end – I am not on until 3pm on Friday !!!

 

 

 

Angular.js in XPages #7 – Writing better code using Services

In this article I will discuss a better programming practice for Angular.js than was demonstrated in the previous articles within this series. I have mentioned before, part of the purpose this blog is very much a “learning in progress” for me. Without going through the previous articles I would not have been able to get to this point and write “better code”. Hopefully with that understanding, those of you who have been along for the ride will appreciate this and grow with me 🙂

Services within Angular

Within Angular there are these programmatic entities called services – and if you read around Angular programming practices (Follow @ToddMotto – http://toddmotto.com/opinionated-angular-js-styleguide-for-teams/) you will find (as I have) that adding application logic within Controllers is generally frowned upon.

“In AngularJS world, the services are singleton objects or functions that carry out specific tasks. It holds some business logic. Separation of concern is at the heart while designing an AngularJS application. Your controller must be responsible for binding model data to views using $scope. It does not contain logic to fetch the data or manipulating it.”

http://viralpatel.net/blogs/angularjs-service-factory-tutorial/

From a maintainability perspective, as well as logical code layout perspective, having functional logic within the controller is akin to writing a 1000 line LotusScript agent  – not optimal !!

I used this article by Dan Whalin to better understand how to create a Service using a factory

So what changed?

We I changed the controller around so that code in the controller that once looked like this:

 $scope.createPerson = function(event) {
            $http({
                url: '//copper.xomino.com/xomino/ainx.nsf/api/data/documents?form=fUserName',
                data: $scope.person,
                withCredentials: true,
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                }
            })
                .success(function(data) {
                    location.href = "#/people";
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
            event.preventDefault();
            return false;
        };

now looks like this:

        $scope.createPerson = function(event) {
            dataFactory.createPerson($scope.person, event)
                .success(function(data) {
                    event.preventDefault();
                    location.href = "#/people";
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

and the service looks like this:

    dataFactory.createPerson = function (person, event) {
        console.log(person)
        return $http({
            url: urlBase+'documents?form=fUserName',
            data: person,
            withCredentials: true,
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            }
        })
    };

 

Now I know you are looking at this and saying to yourself – hang on you just copied and pasted all the code and actually made it longer and more complicated – and that would be very perceptive of you – but (and I had to make this leap to believe it myself), it is better.

My example application only really has one purpose – manage people. In a more complex application a separation of logic into like components would make sense. By using the service I have separated all my “do stuff with people” out into a people Service. When I built the next part of the application – to do with buildings for example I would create a buildings Service and manage all my building logic there.

Let the controller do it’s job and bind the data to the View and keep your logic out of the controller and into Services.

The final Code

My service (service.js)

personApp.factory('dataFactory', ['$http', '$timeout', function($http, $timeout) {

    var urlBase = '//copper.xomino.com/xomino/ainx.nsf/api/data/';
    var dataFactory = {};

    dataFactory.getPeople = function () {
        return  $http.get(urlBase+'collections/name/byFirstName5Col?open&count=3').success(function(data) {
            $scope.people = data;
        });

    };

    dataFactory.getPerson = function (id) {
        console.log('get person')
        return  $http.get(urlBase+'documents/unid/' + id)
    };

    dataFactory.createPerson = function (person, event) {
        console.log(person)
        return $http({
            url: urlBase+'documents?form=fUserName',
            data: person,
            withCredentials: true,
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            }
        })
    };

    dataFactory.savePerson = function (person, event, id) {
        console.log(id);
        return $http({
                url: urlBase+'documents/unid/' + id,
                data: person,
                withCredentials: true,
                method: "PATCH",
                headers: {
                    "Content-Type": "application/json"
                }
            })
    };

    dataFactory.deletePerson = function (id) {
            var temp = confirm('Are you sure you want to delete?')
            if (temp){
                return $http.delete(urlBase+'documents/unid/' + id)
            } else {
                location.href = "#/people"
            }
    };

    return dataFactory;
}]);

My controller

peopleControllers.controller('PersonDetailCtrl', ['$scope', '$routeParams', '$http', 'action', '$timeout', 'dataFactory',
    function($scope, $routeParams, $http, action, $timeout, dataFactory) {

        $scope.create = (action=='new' || action=="delete");

        if (action=="get") {
            dataFactory.getPerson($routeParams.docId)
                .success(function(data) {
                    $scope.person = data;
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        }

        $scope.home = function(){
            location.href='#/people'
        };
        $scope.createPerson = function(event) {
            dataFactory.createPerson($scope.person, event)
                .success(function(data) {
                    event.preventDefault();
                    location.href = "#/people";
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

        $scope.savePerson = function(event) {
            dataFactory.savePerson($scope.person, event, $routeParams.docId)
                .success(function(data) {
                    event.preventDefault();
                    location.href = "#/people"
                })
                .error(function(data, status, headers, config) {
                        console.log('ErrorData: ' + data);
                });
        };

        if (action=="delete") {
            dataFactory.getPerson($routeParams.docId)
                .success(function(data) {
                    $scope.person = data;
                    $timeout(function(){
                        dataFactory.deletePerson($routeParams.docId)
                            .success(function(data) {
                                $scope.person = {};
                                location.href = "#/people"
                            })
                            .error(function(data, status, headers, config) {
                                console.log('ErrorData: ' + data);
                            });
                    }, 300)
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };
    }]);

 

Speaking at MWLUG 2014: Write once, run anywhere – Angular.js in XPages

I am very fortunate and excited to announce that I have been accepted to speak at MWLUG 2014.

MWLUG will always have a fond place in my heart because I had my first speaking session there back in 2012 and had such a fantastic time it pushed me to do more.

I will speak speaking about Angular.js and the opportunity it avails us as Web Developers. We all know IBM Domino is arguably to world’s most secure and mature NoSQL database. So we will take a look at how we can use it like a NoSQL database. Using the power of the data, some REST services and Angular managing the front end functionality, we are able to do some amazing things.

Come and see what Angular can do and how you can write a “Domino” application once and run the front end on any server you like…..

 

Abstract

The business requirement to surface functionality from one application inside of another has long existed and is often difficult to implement. There has always been a gulf between displaying data and creating a functional application. Mark will demonstrate how the power of building a solution based on Angular.js allows an application to be “moved” from Domino to another platform easily and with the bare minimum of code re-write. Taking “Write once, use anywhere” in the literal sense, you will see how to make Domino applications running natively inside of other platforms. Client side frameworks such as Angular.js are the future of web development – come and see it in action.

Angular.js in XPages #6 – A People Manager CRUD application

In this article I will bring together everything discussed in the first 5 articles and demonstrate how to create you first basic CRUD application using Angular.js and XPages.

Previous articles

Introduction

In the last article we saw how routers can be used to not only create meaningful URLs but also control the application functionality. With those routers we are now able to create a functioning CRUD application which will demonstrate the core requirements for building a web based Angular.js application on Domino Data. In this article I will build on the previous article and all those in the rest of the series.

This basic Angular.js/Domino application was created with <160 lines of custom JavaScript code. It will run on Android, iPhone, IE8+, Safari, Firefox, Chrome.

As always – thanks to Dave Leedy for the fakenames.nsf database upon which this is based – and so much more !!

The routing

Inside of the app.js we create our routing table as shown below.

We have a page for the following

  • Viewing all people (/people)
  • Creating a new person (/person/new)
  • Viewing and editing a person (/person/:docId)
  • Deleting a person (/person/:docId/delete)

But as you can see form the code we have the same controller for multiple actions (PersonDetailCtrl). So to differentiate between each action within the same controller we also pass a “resolve” object containing “action: ?” which through the scope, allows us to determine what to do within the controller.

app.js

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

/* App Module */

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

personApp.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.headers.patch = {
        'Content-Type': 'application/json;charset=utf-8'
    }
}])

personApp.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/people', {    //use the people-list.html template, 'PeopleListCtrl' controller and resolve the action: list
                templateUrl: '//copper.xomino.com/xomino/ainx.nsf/partials/people-list.html',
                controller: 'PeopleListCtrl',
                resolve: {
                    action: function(){return 'list';}
                }
            }).
            when('/person/new', {    //use the person.html template, PerconDetailCtrl controller and resolve the action: new
                templateUrl: '//copper.xomino.com/xomino/ainx.nsf/partials/person.html',
                controller: 'PersonDetailCtrl',
                resolve: {
                    action: function(){return 'new';}
                }
            }).
            when('/person/:docId/delete', {    //use the person.html template, the PersonDetailCtrl controller and resolve the action: delete
                templateUrl: '//copper.xomino.com/xomino/ainx.nsf/partials/person.html',
                controller: 'PersonDetailCtrl',
                resolve: {
                    action: function(){return 'delete';}
                }
            }).
            when('/person/:docId', {    //use the person.html template, the PersonDetailCtrl controller and resolve action: get
                templateUrl: '//copper.xomino.com/xomino/ainx.nsf/partials/person.html',
                controller: 'PersonDetailCtrl',
                resolve: {
                    action: function(){return 'get';}
                }
            }).
            otherwise({    //if all else fails then just go to people
                redirectTo: '/people'
            });
    }]);

The controller

Within controller.js we can see the two controllers peopleControllers as we had before

  peopleControllers.controller('PeopleListCtrl', ['$scope', '$http',

and

  peopleControllers.controller('PersonDetailCtrl', ['$scope', '$routeParams', '$http', 'action',
    function($scope, $routeParams, $http, action) {

Within the PersonDetailCtrl we now some IF statements – depending on what resolve variable is passed into the controller, we determine what action to take.

The resolve parameter passed in is “action:” and that is added to the controller as a parameter ‘action’. Using this ‘action’ we know the users intention.

We also create two scoped functions $scope.createPerson and $scope.savePerson. These actions will be linked into button clicks as we will see later in the article.

controller.js

/**
 * Created by mroden on 5/05/2014.
 * With help from - http://scotch.io/tutorials/javascript/creating-a-single-page-todo-app-with-node-and-angular
 */

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

peopleControllers.controller('PeopleListCtrl', ['$scope', '$http',
    function ($scope, $http) {
        $http.get('//copper.xomino.com/xomino/ainx.nsf/api/data/collections/name/byFirstName5Col?open&').success(function(data) {
            $scope.people = data;
        });
    }]);

peopleControllers.controller('PersonDetailCtrl', ['$scope', '$routeParams', '$http', 'action', '$timeout',
    function($scope, $routeParams, $http, action, $timeout) {

        console.log("Action1: "+action);
        $scope.create = (action=='new' || action=="delete");

        if (action=="get") {    //get the user data from the DDS Document REST service
            $http.get('//copper.xomino.com/xomino/ainx.nsf/api/data/documents/unid/' + $routeParams.docId)
                .success(function(data) {
                    $scope.person = data;
                    console.log($scope);
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        }

        $scope.createPerson = function() {   //POST the $scoped person data to the DDS and create a new document
            $http.post('//copper.xomino.com/xomino/ainx.nsf/api/data/documents?form=fUserName', $scope.person)
                .success(function(data) {
                    location.href = "#/people"
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

        $scope.savePerson = function() {    //Create a PATCH request and send the updated $scoped person data to the DDS Document
                $http({
                    url: '//copper.xomino.com/xomino/ainx.nsf/api/data/documents/unid/' + $routeParams.docId,
                    data: $scope.person,
                    method: "PATCH"
                })
                .success(function(data) {
                    location.href = "#/people"
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

        if (action=="delete") {   //Get the person data - then delay slightly - then as the user if they want to delete
            $http.get('//copper.xomino.com/xomino/ainx.nsf/api/data/documents/unid/' + $routeParams.docId)
                .success(function(data) {
                    $scope.person = data;
                    console.log($scope);
                    $timeout(function(){  //delay 300ms and then ask the user to confirm deletion
                        var temp = confirm('Are you sure you want to delete?')
                        if (temp){    //if yes delete then send a DELETE request to the DDS Document REST service
                            $http.delete('//copper.xomino.com/xomino/ainx.nsf/api/data/documents/unid/' + $routeParams.docId)
                                .success(function(data) {
                                    $scope.person = {};
                                    location.href = "#/people"
                                })
                                .error(function(data) {
                                    console.log('Error: ' + data);
                                });
                        } else {
                            location.href = "#/people"
                        }
                    }, 300); // time here

                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });

        };

    }]);

Update people-list.html

We update the people-list with two new “buttons”. An “Add Person” button and a “Delete” button.

a1

Both buttons route to person.html, but the DELETE option loads the person’s information.

If we look in app.js we can see that #/person/new sends the action: new to the controller. But in the controller we are not actually looking for a “new” action. At initial glance though in the controller we are not looking for if (action==’new’). But we are doing is using it for something subtle but no less important.

        $scope.create = (action=='new' || action=="delete");

What we are doing is simply setting up a scope$ variable for “create”. When we come to the person.html template we will see why.

people-list.html

<pre><div class="row" style="width: 50%">
    <div class="col-sm-2">
        <a class="btn btn-default" href="#/person/new">Add Person</a>
    </div>

</div>
<table class="table table-striped">
    <thead>
    <tr>
        <th>First Name</th><th>Last Name</th><th>Zip</th><th></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>
        <td><a class="btn btn-warning" href="#/person/{{person['@unid']}}/delete">Delete</a></td>
    </tr>

    </tbody>
</table>

Deleting a Person

Clicking the Delete Button performs the following:

  • Changes the URL to #/person/UNID/delete
  • Which is picked up by the router
    • Which uses the personController
    • that then executes the When(‘/person/:docId/delete’, {
    • Which uses the person.html
    • Which in turn resolves the action: delete
  • $scope.create is set to true
  • The person “form” is loaded and filled with data from Domino Data Service
  • Pause for 300 milliseconds using $timeout
  • The controller then acts on the delete action
    • if (action==”delete”) {
    • Confirms do you want to delete
    • var temp = confirm(‘Are you sure you want to delete?’)
    • IF true then loads the http DELETE at the DDS URL for the document
    • $http.delete(‘//copper.xomino.com/xomino/ainx.nsf/api/data/documents/unid/’ + $routeParams.docId)
    • and returns the user back to the original list
    • location.href = “#/people”

a2

 

Adding a new person

When we click the Add person button:

  • The URL is changed to #/person/new
  • The router picks up the change and loads person.html
  • resolves the action: new
  • $scope.create is set to true
  • The person.html template is loaded with no data – there is no action in the controller associated with new to load any

ng-show / ng-hide

We brushed over the $scope.create for delete but lets now look at it in the context of a new Person. Looking at the form for editing  person we see a “Create” button whereas for an existing person we see a “Save” button. Looking at the code for the two buttons closely we can see two new directives – ng-show and ng-hide

    <div class="col-sm-2">
        <button class="btn btn-default" ng-click="savePerson()" ng-hide="create">Save</button>
        <button class="btn btn-default" ng-click="createPerson()" ng-show="create">Create</button>
    </div>

The ng-show and ng-hide directives are used in conjunction with the $scope.create to appropriately show and hide the buttons. So with no JavaScript code written we are able to apply a programmatic hide/when to the buttons based on the $scope data. That’s cool !! 🙂

a4

Once the data is entered and we click create – the ng-click directive kicks in and calls the “createPerson()” function. Back in the controller we created the $scoped createPerson() function…

        $scope.createPerson = function() {
            $http.post('//copper.xomino.com/xomino/ainx.nsf/api/data/documents?form=fUserName', $scope.person)
                .success(function(data) {
                    location.href = "#/people"
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

In this function we perform a “POST” at the DDS api/data/documents?form=fUserName which creates a document based on the fUserName function. The data POSTed is the $scope.person. We can see the JSON using firebug sent back to the server. We are then returned to the people-list and can see the new entry loaded.

a3

Editing an existing person

When clicking on the Edit button from the people-list.html template a person is loaded as we saw in the previous article. Looking back at the person.html “Save” button which is now visible because $scope.create is not equal to true – we can see the save button uses ng-click to call the “savePerson()” function. Once again we look to the controller.js and see where this was created.

        $scope.savePerson = function() {
                $http({
                    url: '//copper.xomino.com/xomino/ainx.nsf/api/data/documents/unid/' + $routeParams.docId,
                    data: $scope.person,
                    method: "PATCH"
                })
                .success(function(data) {
                    location.href = "#/people"
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });
        };

In this case we are using the calling $http using a slightly different pattern. I want to do a PATCH to only modify the data which has changed within the document. Out of the box Angular does not have a $http.patch method so we have to create the http call manually. We provide the url, data and method as shown above. To allow this on the server we have to enable the PATCH method within our Domino Website document (a sign that you have not got this enabled will be a 405 method not permitted error).

a5

Once we click the Save button we can see the JSON being sent to the server, using the PATCH method, and we are then routed back to the front page.

a6

Here’s a question for you guys – as you can see from the PATCH the data passed back a lot more information than just the firstname, lastname and zip  – why and where did the rest of the @fields come from??

🙂

person.html

<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}}" ng-model="person.firstname">
    </div>
    <div class="col-sm-4">
        <label>Last Name</label><br/>
        <input class="form-control" name="lastname" type="text" value="{{person.lastname}}" ng-model="person.lastname">
    </div>
    <div class="col-sm-4">
        <label>Zip</label><br/>
        <input class="form-control" name="zip" type="text" value="{{person.zip}}" ng-model="person.zip">
    </div>
</div>
        <br/>
<div class="row" style="width: 50%">
    <div class="col-sm-2">
        <a class="btn btn-default" href="#/people">Back</a>
    </div>
    <div class="col-sm-2">
        <button class="btn btn-default" ng-click="savePerson()" ng-hide="create">Save</button>
        <button class="btn btn-default" ng-click="createPerson()" ng-show="create">Create</button>
    </div>
</div>
<hr />

In this article the index.html did not change from the last article

Thoughts and Questions

We have Created, Read, Updated and Deleted data using Angular.js to control the Domino Data Services REST service. Have we created an XPages application? In the literal sense of the word(s), no I don’t think we have.

  • We have not created a single XPage
  • We have not used an XPages control
  • We have used out of the box DDS which comes with the server not with XPages.

So this is just a Domino application then?

  • We used Domino Data Services
  • Our data is coming from an IBM Domino database
  • We didn’t use any XPages or code which has to be pre-compiled on the server
  • We did however take advantage of the fact that this is a >R8.5 server/database which allows access to Package Explorer and syncing of an ODS
  • Sooooo – yes it is just a Domino application but not the one you grew up with 15 years ago

Could we have written it quicker in XPages?

  • We I certainly could have at the beginning, because I had to learn Angular as I was doing it – but that wasn’t the point of the question was it….
  • Yes we could have created it with less “XPages” code. IBM has provided some nice tools to allow us the “developer” to take action on data without having to think to hard about the inner workings of it.
  • That being said though, I believe an experienced Angular developer would be able to put this together just as quick as an experienced XPages developer.

So then what have we gained Marky?

  • Well I for one can now put “Angular.js” on my résumé as something I have at least a basic understanding of
  • Understanding how a web based application like this interacts with a NoSQL system gives me more flexibility in problem solving customer requirements
  • This application uses the best NoSQL database system in the world – that is never a bad thing
  • There are actually some other major advantages to programming an application in this manner. I am working on them for a future demonstration and presentation at MWLUG (teaser intended) – All in good time 🙂

Overall, the term “XPages development” seems to encompass more than just the out of the box “XPages” tools which IBM have provided . It has (to me anyway) come to mean the whole server package and not just XPages tooling. With that has come a broadening of the mind into truly modern web development using the modern Domino development platform – I am perfectly OK with calling this “Angular.js in XPages”.

Conclusion

Over the last few article we have opened the covers on the new web technology that is “Angular.js”. We built a CRUD application using data from arguably the world’s most secure NoSQL system. I love it 🙂

Download the example

The example can be downloaded from this link – but some work will need to be done to make it work on your server:

  • Change the URLs in app.js and controller.js to your server
  • Make sure you have PATCH enabled on your server website document (requires restart)
  • You must enable Domino Data Services on your website document
  • You must ensure DDS access to your database (DB Properties)
  • You must enable DDS access to your view
  • You will find all the files in the Package Explorer / WebContent folder for the database.

PS

For the PATCH question I asked – here’s a tip – what’s in $scope ?

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

Angular.js in XPages #4 – Using Domino Data

In this article I will demonstrate how to use Angular.js to access Domino Data and display it in place of the hard coded data we used in the previous article.

Introduction

This article is based off the Angular developer tutorial on XHR and dependency injection . It is essential you go and read that article before continuing. It explains how dependency injection works, why it is used and about Angular services.

Modifying the controller

In our previous article we used a hard coded json string of data directly within the controller. In a real application though, the data will be dynamic and need to be pulled directly from the data repository as and when needed.

Injecting the $http service

When the controller is constructed we are able to pass to it an unspecified number of services which the controller code are dependent on. In a sense it is just like creating your XPage, needing to use jQuery and making sure the jQuery library is loaded on the page before you use a $(‘selector’). The jQuery library is a dependency of the XPage code running the plugin.

The previous controller looked like this

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

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

personApp.controller('PersonCtrl', function ($scope) {
    $scope.people = [
        {
            "@entryid":"1-431C28F894B0548185257C22000A8403",
            "firstname":"Agnes C.",
            "lastname":"Smith",
            "address":"24256 Leisure Lane"
        },
        {
            "@entryid":"2-4ED02366A420D23985257C4F000B0BD7",
            "firstname":"Albert",
            "lastname":"Scoggins",
            "address":"3525 Koontz Lane, second"
        },
        {
            "@entryid":"3-6D8120FA75B4BCCF85257C4F000B0B5D",
            "firstname":"Alejandra",
            "lastname":"Dodge",
            "address":"3044 Simpson Avenue"
        }
    ];
});

And out new controller looks like this

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

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

personApp.controller('PeopleCtrl', function ($scope, $http) {
    $http.get('http://copper.xomino.com/xomino/ainx.nsf/api/data/collections/name/byFirstName5Col?open&')
         .success(function(data) {
            $scope.people = data;
         });
});

So you can see that the $http service has been added as a service added to the instantiation of the personApp controller PeopleCtrl. The code has also obviously got much shorter because he are not hard coding the data in here.

**yourDatabaseJSONFeed**

In Domino (R9) we have a number of methods available to us to create JSON data securely:

  • LotusScript/Java agent
  • Form with dataType set
  • XAgent (SSJS and/or Java)
  • ExtLib REST service (out of the box and custom REST)
  • Domino Data Services

For the sake of these examples I am going to use Domino Data Services (DDS). For more information on what they are and how to set them up securely in your database check out these links:

In the example above we used the Domino Data Services the URL for provides the following

http://copper.xomino.com/xomino/ainx.nsf/api/data/collections/name/byFirstName5Col?open&#038;

[
  {
      "@href":"\/xomino\/ainx.nsf\/api\/data\/collections\/name\/byFirstName5Col\/unid\/C5543E624FF5042805257838007ABDA3",
      "@link":
      {
          "rel":"document",
          "href":"\/xomino\/ainx.nsf\/api\/data\/documents\/unid\/C5543E624FF5042805257838007ABDA3"
      },
      "@entryid":"1-C5543E624FF5042805257838007ABDA3",
      "@unid":"C5543E624FF5042805257838007ABDA3",
      "@noteid":"18D6",
      "@position":"1",
      "@siblings":1298,
      "@form":"fUserName",
      "firstname":"Adam",
      "lastname":"Saenz",
      "address":"2519 Custer Street",
      "city":"ROCKWOOD (SOMERSET)",
      "state":"PA",
      "zip":15557
  },
   .......

And as you can see from this the firstname, lastname and address fields are supplied in the REST JSON feed.

Note on testing

If you try and load the code up in the Webstorm debugger you will get the following CORS problem – nothing works…..

a1

When your Browser is in the “localhost” domain it cannot pull ajax requests from the xomino.com domain – it is a security feature of the browser and server to prevent cross site scripting. Read more about CORS here.

This is why (as we saw in the previous article) we integrated the development environment with the Domino ODS. Because I am working on a sync’d version of the database – all I have to do is go over to the index.html within the Domino database on my server and it works (because we are in the correct domain)

a2

 

Conclusion

What we have seen in this article is a simple evolution of the controller to allow it to pull real data from the server rather than using hard coded Data. In the next article we will look at further CRUD operations.

 

Angular.js in XPages #3 – The first app

In this article I will recreate one of the simple Angularjs.org developer tutorials and relate it to how we would build an equivalent XPages application.

Create the database

As described in the previous article, set up a Domino database with an ODS. Then link your Webstorm to the new ODS structure.

Download the Angular code from https://angularjs.org/ it will be an 8M download. Opening the zip file you will see a lot of “angular files”. These are “Dependency files” and are separate functional areas of Angular which can be included within your application as necessary. Although programatically not the same, the concept is similar to the way we use Dojo in XPages.

a1

For the moment we are going to add the following to our database. for the sake of clarity I have added them in an angularjs folder in the webContent folder of my database. I have also added bootstrap (to keep Mark Leusink happy).

a2

I then move over to my Webstorm and add the project

a3

 

CDN alternate

You absolutely do not have to download the files and have them reside locally – you can link to the angular code through the Google CDN site. This is entirely up to you.

Quick MVC

MVC stands for “Model View Controller” and unlike traditional Domino development, forces good coding practices and separates UI components from application logic. This methodology has been brought closer to reality in XPages development using good Java coding practices but is still rather elusive for those using SSJS.

  • The Model is the data created by the application
  • The View is the HTML representation of the data through the use of Templates
  • The Controller is  the application code which is used to determine the way the Model is populated and displayed.

 

Our first Angular app

I am going to recreate the Mustache.js article I wrote earlier this year and show how to use a front end Angular repeat to create a basic page of data.

I am not going to repeat everything which you can read for yourself in the Angular tutorial. The tutorials are great and frankly better written than I can. So with the assumption that you at least understand the terms I will attempt to explain what I am doing.

Create Our HTML File 

I then create a new angular HTML file in Webstorm by right clicking on the WebContent and creating new HTML file. (You will notice that you can actually create your own Template so that in the future you can have a preset angular shell set up and ready to go if you like !)

a4

 

The View and Template

Within the HTML file we are going to create a simple DIV and manually attach a controller to it using an angular directive. The ng-controller in this case tells the Angular code to “Do your stuff Here”. Kinda like an <xp:panel> where we control a section of the functionality without affecting the rest of the XPage.

a1

You will also notice that I added ng-app =”personApp” to the HTML tag at the top of the page. That associates the application with this whole page. More on that later.

Within the PersonCtrl we are going to add our template code (once again akin to what we did in the mustache example except there we used a <script> tag to contain our template HTML)

a1

So there is a little to talk about here.

The Repeat control

The Angular “repeat control” is an element (any HTML element) with the ng-repeat in it. In this case we are looking to repeat all Person within People (coming from the controller in a minute). Seems simple enough to read and understand

        <div ng-repeat="person in people">
        ...
        </div>

 The data binding

                    {{person.firstname}} {{person.lastname}}

The data-binding using the same {{ STUFF }} notation as the mustache example. Double Curly Brackets denotes the template placeholder for *DATA HERE*

The Model and Controller

We are going to create a new directory and within it the controller.js file. That file contains the information necessary to create the page we are looking for.

The controller contains two parts

  1. The code which runs Angular in the first place
  2. The controller code which says put this data *there*

 

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

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

personApp.controller('PersonCtrl', function ($scope) {
    $scope.people = [
        {
            "@entryid":"1-431C28F894B0548185257C22000A8403",
            "firstname":"Agnes C.",
            "lastname":"Smith",
            "address":"24256 Leisure Lane"
        },
        {
            "@entryid":"2-4ED02366A420D23985257C4F000B0BD7",
            "firstname":"Albert",
            "lastname":"Scoggins",
            "address":"3525 Koontz Lane, second"
        },
        {
            "@entryid":"3-6D8120FA75B4BCCF85257C4F000B0B5D",
            "firstname":"Alejandra",
            "lastname":"Dodge",
            "address":"3044 Simpson Avenue"
        }
    ];
});

Creating an angular module

The angular.module code in the example above makes the HTML tag with ng-app=”personApp” (The HTML tag) into the module we are creating. In more complex applications there are going to be multiple modules in multiple areas of the page but in this case nice and simple just the one.

The controller binds to PersonCtrl (our DIV ng-controller=”PersonCtrl”) and then returns the $scope.people data to it as a function.

$scope is a special name for the angular scope within the defined module. It contains all the information abou tthe module but does not have access to anything outside of it. In this case we are injecting the data into the application at load time so that it can be displayed.

The results

Loading the application up in the debugger we can see the result we are looking for

a2

Back in Domino ?

I went back over to DDE and it has refreshed automagically – There are my new files on the ODS and synced to the server database

a3

I opened the index.html on my server and with ZERO additional effort I now have my simple app in a secure database requiring login – nice eh 🙂

a5

Conclusion

In this short article I have recreated one of the simple examples from the Angular.js developer tutorials and tried to relate it to the XPages development environment. In this case we have static JSON which is a nice example to start with, but not really application-worthy. in the next article we will look at getting real data from the application.

 

PS

Yeah I noticed in review that “Hello My is Mark Roden” is not the most correct English I have ever used in an example – but the code still works 🙂

Angular.js in XPages #2 – Setting up a Webstorm / Domino development environment

In this article I will show how to set up Domino in a flexible manner to store our Angular applications, while still being able to use Webstorm as our development platform.

Creating an Angular App in a Domino database

All credit for this idea goes to my boss Andrew Barickman – he came up with the idea and it is genius 🙂

Ultimately we want to create an application which resides within the security model of a Domino database with as little, if any, effort as possible. In a large part this means access control. We are going to look at creating an HTML file within the WebContent area, within a Domino database – but that causes pain and suffering if I want to use Webstorm. Webstorm cannot access a file which is held within the database directly.

So what we are going to do is create an On Disk project for the database as if we were going to use Source Control (which you should do anyway).

ws1

Once located on the disk we can then point Webstorm at the On Disk Project

ws33

In Domino Designer make sure the following is set

  • Build Automatically
  • Auto refresh set to be on
  • AutoSync Source Control

a1

ws3

Starting with this page

ws5

We add “Marky” to Webstorm and SAVE

ws6

Note: If you do not have refresh automatically turned on and you actually try and open the file through DDE you will see an error – if you see this, turn on refresh automatically 🙂

ws7

 

If you see the error – Hit F9 – which cause the refresh / sync to occur

ws8

And it is pushed up to your database copy

ws9

With auto refresh and sync turned on it may take a few seconds to make it up to your dev server. It then properly resides within Domino’s access security model.

This method also works from DDE back to Webstorm – if you make a change in DDE it will automatically update the Webstorm file if it is open.

Other approaches

I know Mark Barton has a different Domino/Webstorm setup and he has blogged about that setup himself….. 🙂

http://www.markbarton.com/?p=373

In the mean time in the next article we will look at creating our first very basic app.

Angular.js in XPages #1 – Using the right IDE for development

In this article we will look at Webstorm (a Javascript IDE) and discuss how that relates to the traditional XPages development environment.

Tools

Better Tools make better applications (or something like that). It’s true to an extent, you can’t help a poor developer make a great application but with better tools you can make a good developer more productive, which ultimately makes for a better application (probably). In the XPages development world the tooling we have is all based around Domino Designer in Eclipse (DDE). Eclipse as we all know is an open source tool which while originally designed for Java development has been extended to cover development of many programming languages. There are XML editors, JavaScript editors and many others. In the XPages world we are also “stuck” with quite an old version of Eclipse and are unable to take advantages of newer capabilities and plugins for modern eclipse development. DDE “works” but it is far from a fantastic experience, especially from a “web development” perspective. In these articles I will not be using DDE very much at all. We are going to look at Web Development using Domino Data, not doing web development using Domino.

I am no expert on Java development using DDE but from what I have picked up there are others who are doing their pure java development outside of DDE and then importing it into DDE to run in their applications. For power-developers stability and speed are just as critical as flexibility and practicality.

Web development environments

There are a number of tools currently en vogue for web developers. SublimeText is a very powerful text editor and Github have release a new “Atom” editor which is getting a lot of press right now as well. I am going to use JetBrains Webstorm for these articles. Webstorm is a great IDE and comes with a bonus of having Angular integration built in.

Webstorm

Webstorm is not a free tool – you have to buy a copy – but IMHO it is worth it. $50 for a personal license is about 1/2 hour of your productivity and it will pay for itself inside of a few days. There is a 30 day evaluation period which I encourage you to try 🙂

For a full list of examples check out the demo page – http://www.jetbrains.com/webstorm/documentation/ but here are some of the cool examples:

  • Angular integration into HTML ws2
  • Type ahead JS ws3
  • Type Ahead CSS ws1
  • Code formatting
  • Keyboard shortcuts (TOTALLY AWESOME) – https://www.youtube.com/watch?v=PNZJox8pkls
  • Debugging and live text editing using a Chrome extension

Why not use DDE and XPages?

Well first and foremost because an “XPages” is an XML document and because of that, DDE requires it to be written using correctly formed XML. HTML is not correctly formed XML and causes issues. Let me demonstrate: If I want to create the following HTML inside of DDE I get an error:

<div enter exit>

</div>

ws4 Seems simple enough but we really should use the right tool for the right job.

This is particularly important when we start to look at Angular directives which rely (when not using the HTML5 standards) on the single word attribute in the HTML element.

Secondly, as you will see from the examples in the upcoming articles I will be creating HTML pages not XSP pages. These can still reside inside of the database but are not “XPages”. I will not be using XPages “controls” and in fact not using Dojo either. So there is no benefit to using DDE at all in this case for my front end development. I will be using DDE to create REST services (maybe) and I would use DDE to create Java code to supply data to the user.

Finally –  Eclipse is a large tool and is frankly slower than me. It’s a frustrating when I constantly have to find something to do while a database waits to open, checks mail, Builds something, crashes in a corner quietly. I won’t miss using it.

Working locally

Even if we are not working with a Domino server, we still need to be able to review and test what we are working on.

In Webstorm you can preview your code as Webstorm generates it’s own local basic web server. The web server is configurable and as we will come to see it will help us in our Domino development environment as well.

ws10 ws11

 

Debugging

Webstorm has an internal debugger (similar to chrome dev tools and firebug) and you can debug the code with the JetBrains Chrome extension

ws1

One really nice feature about the debugger is that it allows Live editing, so you can edit and build you code and watch in real time as the code is updated on the screen. This is very cool when you are building out your HTML templates. I this case on a large monitor I have the IDE on the left and the browser on the right. You can see this technique in action on the http://egghead.io angular tutorials.

ws2

Starting to type into the debugger window you see the HTML updated on the right – Angular does not update the {{phone snippet}} because the template has to be redrawn. HTML on the other hand is redrawn immediately

ws3

Hitting F5 refresh in the browser reloads the template without having to save the HTML on the left

ws4

As we will come to see this will allow us to take data from Domino without worrying about CORS issues. For more on the debugger read here – http://www.jetbrains.com/webstorm/webhelp/using-jetbrains-chrome-extension.html

As we get more into the demonstrations we will look at getting data from the notes database and how that works practically on a localhost

In the next we will look at a basic overview of some Angular concepts – in the mean time check out the developer tutorials

Conclusion

Even in the short time I have been using Webstorm I have found it to be an exceptionally quick development environment compared to DDE. DDE certainly has its place but even if I am not using angular for future projects I am seriously going to consider doing as much of my development using Webstorm rather than DDE.

Tooling is a very personal thing for a developer, in this case when I am approaching the project from a Web Development perspective and creating web pages without the overhead of the JSF lifecycle, there really is no need to use DDE to build these examples. I am going to use XPages for data sourcing and CRUD.

*Disclaimer*

I do not want this to become a discussion of whether this approach or not this is right or wrong and I encourage everyone to consider their comments before posting. The point of this series is for education and mind broadening, driven by my own selfish desire to learn and broaden my mind.

  • Yes there are many great things I am giving up by not using DDE and XPages controls
  • But I am also going to gain a lot of things which XPages does not allow me to do easily or gracefully

*/Disclaimer*

 

PS

Just so you know Jetbrains also make a Java IDE which is used for android development – http://www.jetbrains.com/idea/ – another alternate to eclipse. Again it isn’t free, but may be worth checking out because free doesn’t mean the best (and visa versa).