Tuesday 13 May 2014

The magic of $resource

If you look at demos around using $resource in Angular then you'll see code something like this

app.factory("issuesResource", function ($resource) {
    return $resource('/api/issues/:id', {
        id: "@id"
    }, {
        update: {
            method: 'PUT'
        }
    });
});
so, what does this do/mean.

Breaking it down:

app.factory
creates a factory. Normally you would see something like this:
app.factory("myFactory", function(){
    return {
      doSomething: function(){}
    };
});
so when creating a $resource based service the code looks a little odd, it returns $resource(), so what does the call to $resource do?

$resource is "A factory which creates a resource object" according to the documentation, so what our code does is to take $resource (already a factory) and wrap it in our factory and then extend it. We extend it by telling it what the base URL is ('/api/issues' in this case), telling it where to get an 'id' value from when needed (more on this is a moment) and extending it with our own methods.

The last object passed to $resource

update: {
   method: 'PUT'
}
adds an 'update' method to $resource and causes $resource to use an HTTP PUT when this method is called.

The other parameter

{
    id: "@id"
}
is more interesting. According to the documentation then: "If the parameter value is prefixed with @ then the value of that parameter is extracted from the data object (useful for non-GET operations)." So this implies that the parameter is not used for GETs, and indeed this is so. I have a spec that looks like this:
it("should return an object when get is called", inject(function (issuesResource) {
    httpBackend.expectGET("/api/issues/1").respond(200, {id: 1});
    var issues = issuesResource.get({id: 1});
    httpBackend.flush();
    expect(issues.id).toBe(1);
}));
and this works whether the '@id' parameter is in place or not. For GETs it's the name of the placeholder in the URL that matters, so the 'id' in the '/api/issues/:id' has to match the 'id' in the call to
issuesResource.get({id: 1});
, however this is not true for POST, PUT or DELETE. In these cases the presence and name of the '@id' value is important.

So if we have a test for POST spec that looks like this:

it("should use the id passed in the object when posting", inject(function (issuesResource) {
    httpBackend.expectPOST("/api/issues/1").respond(200, {id: 1});
    var issues = issuesResource.save({id: 1});
    httpBackend.flush();
    expect(issues.id).toBe(1);
}));
then in the call to save the name of the property ('id') must match the name defined when creating the factory.

Angular application layout

Most of this I've got from John Papa's Pluralsight course on Breeze and Angular, some is my own, I wanted to write it as a step-by-step otherwise I forget it:
  1. Install all the libraries using Bower

    Bower is a package manager for the web, using this makes it easier to manage dependencies and general stuff around packages such as updates. I install all the libraries I can find in Bower such as Angular (and it's sub libraries such as cookies and resources), jQuery, moment etc...

    I have a .bowerrc file that specifies the directory I put the packages into, mine go into public/bower

  2. Create an 'app' directory. This will contain all my application code and will be split into a set of subdirectories
    • The root contains the startup code, routing configuration, common configuration (such as events and the actual app loading code) and error handling code
    • common: contains shared code, such as logging
    • layout: contains the layout files used across the site (sidebars, navigation etc.)
    • [others]: one subdirectory per location, 'home', 'admin' etc
    • services: contains all the application's 'services' including the directives the application uses
  3. Each 'location' contains the HTML, controller and service needed for that functionality. This means that the 'authentication' section will have the layout (but not the directives), controller and service that authentication needs to work. This provides, at least, a location aware form of packaging. Should the controller and service be loaded into their own module, I'm not sure yet. On the one hand that seems like overkill, but on the other hand it may mean better isolation when testing
  4. Load all code as IIFEs. Each component is then self loading, it does all the steps necessary to set itself up within angular (such as registration). The code looks something like this:
    (function () {
        'use strict';
        var controllerId = "IndexController";
    
        angular.module('app').controller(controllerId, ["$scope", "common", 'someService', IndexController]);
    
        function IndexController($scope, common, someService) {
            var vm = this;
    
            activate();
    
            function activate() {
                common.activateController([], controllerId)
                    .then(function () {
                        log('Activated Main Page');
                    });
            }
    
        }
    }());
    
    plus all the other code the controller needs of course. Again this feels like a reasonable approach, there is a 'standard' activate method (that calls a common activateController method) that can be used to wire up events and/or data after the controller and its services have been fully loaded.

Modularisation is one of the things I'm still unsure about. It feels that putting everything into the 'app' module is wrong, it also feels wrong to put each functional area into its own module; maybe have the an 'app' module, a 'controllers' module and a 'services' module, still to be decided I think.

Friday 22 November 2013

Knockout value binding and valueUpdate

I'm part of a team writing a Single Page Application, a misnomer if ever there was one. The client code uses knockout.js to bind data to the HTML. The application supports desktop browsers (from IE8 and up) but also has to run on mobile devices, in particular on iOS. Recently we've hit a couple of iOS issues, one involving SignalR and one involving knockout. Both of these appear to be bugs (features) introduced in iOS7.

The knockout problem appears when you use the valueUpdate:'afterkeydown' parameter on the value binding and has been reported here. As this affected us I needed to find a workaround (not the point of this post) and had to understand how valueUpdate works (which is the point of this post).

Id you read the knockout documentation around valueUpdate it says

If your binding also includes a parameter called valueUpdate, this defines additional browser events KO should use to detect changes besides the change event. The following string values are the most commonly useful choices:
  • "keyup" - updates your view model when the user releases a key
  • "keypress" - updates your view model when the user has typed a key. Unlike keyup, this updates repeatedly while the user holds a key down
  • "afterkeydown" - updates your view model as soon as the user begins typing a character. This works by catching the browser’s keydown event and handling the event asynchronously.
which I took to mean "you can only use these three properties, which now that I've re-read it, isn't what it says at all.

You can pass the name of any event to valueUpdate and an event handler will be added to the control for that event, the controls value will then be update when that event fires. Furthermore you can make this event handling asynchronous by prepending the 5 letters 'a', 'f', 't', 'e' and 'r' to the event name, i.e. afterKeyDown, afterInput etc. You can also pass an array of events to listen on to valueUpdate, something like: valueUpdate:['propertychange','input']. The code that handles valueUpdate looks something like this (in knockout 2.3)

var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
var propertyChangedFired = false;
if (requestedEventsToCatch) {
    if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
        requestedEventsToCatch = [requestedEventsToCatch];
    ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
    eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
}

var valueUpdateHandler = function() {
    // update the value
    // ...
}

// Workaround for https://github.com/SteveSanderson/knockout/issues/122
// IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
// CODE NOT SHOWN HERE

ko.utils.arrayForEach(eventsToCatch, function(eventName) {
    // The syntax "after" means "run the handler asynchronously after the event"
    // This is useful, for example, to catch "keydown" events after the browser has updated the control
    // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
    var handler = valueUpdateHandler;
    if (ko.utils.stringStartsWith(eventName, "after")) {
        handler = function() { setTimeout(valueUpdateHandler, 0) };
        eventName = eventName.substring("after".length);
    }
    ko.utils.registerEventHandler(element, eventName, handler);
});

The code shown here does two things. The first is to take the list of events passed to valueUpdate and create an array of event names. The second is to bind those events to the control. It's the binding code that's interesting. There is an event handler (the function is shown but the code in that function has been elided) called valueUpdateHandler. In the arrayForEach loop, this handler is bound to the event unless the name of the event starts with 'after'.

In this latter case a new function is defined that simply calls setTimeout and this function is bound to the handler. The setTimout has a timeout of 0 and the valueUpdateHandler as the callback function. This means that when the event fires the timeout code is executed and this immediately queues the real event handler to run. This event handler then runs when the main thread is available

This means that any event that the control supports can be bound to the valueUpdate and any event that is bound can be run asynchronously. Which is pretty cool.

(We ended up binding afterinput along with afterpropertychange (to support IE8))

Thursday 8 August 2013

Authentication in AngularJS, node.js and passport

I'm writing this as much for my own good as a reminder of how I got to here as a newbie Angular and node developer. The aim was to get an Angular app authenticating with a node.js server using passport middleware. I wanted to use a directive on the Angular side to manage the UI and have the Angular code layered so that I could plug in different authentication schemes if I needed to.

Passport is node 'middleware' that manages authentication for an application, the code currently uses 'local' authentication as this is simply a learning exercise.

On the client I created an Angular directive that shows the UI and calls a controller that manages the authentication. The controller looks like this:


AuthenticateCtrl = function ($scope, $http, $location, $cookies, authenticate) {

    var isSignedIn = false;

    $scope.isError = false;

    $scope.isSignedIn = function () {
        return authenticate.isAuthenticated;
    };
    $scope.signout = function () {
        var promise = authenticate.signOut();
        promise.success(function () {
            authenticate.isAuthenticated = false;
        }).error(function () {
        });
    };
    $scope.signin = function (user) {
        var promise = authenticate.signIn(user);
        promise.success(function (data, status, header) {
            authenticate.isAuthenticated = true
            $location.path( "/" );
        }).error(function () {
        });
    };
    $scope.register = function () {
        var promise = authenticate.register();
        promise.success(function (data, status, header) {
        }).error(function () {
        });
    };
};

The controller offers three methods and a property to the directive (this controller is used by the authentication directive). The isSignedIn property is used by the directive to manage the parts of the UI that are shown. The signIn method is where the action takes place. The controller is injected with an 'authentication' service and it's this service that does the work. The controller expects the service to have similar signIn and signOut functions and that these functions return promises. The code is written this way as I want the service to carry out the logic of the authentication process but I want the controller to be in charge of what to do on success/failure as this is a UI consideration. Here the controller sets a flag for the directive to use and also sets the browser location so that the app navigates if the authentication is successful.

Next the authentication server code:

authenticate = function ($http, $cookies) {

    var authenticated = $cookies.user ? true : false;

    var signIn = function (userData) {
        if (userData) {
            var promise = $http.post("/authenticate/local", userData);
            return promise;
        }
    };

    var signOut = function () {
        return $http.get("/logout");
    };


    var register = function () {
    }

    return {
        signIn: signIn,
        signOut: signOut,
        isAuthenticated: authenticated,
        register: register
    }
}

Surprisingly simple.

The code calls $http.post against a specific URL and returns the promise from that call, the URL should probably be parameterised. So far, so straightforward.

The only slightly odd thing is the way that the authenticated value is initialised, var authenticated = $cookies.user ? true : false;. It looks for a cookie called user and if that exists assumes that the user is authenticated. That is done for the case where the app is initialised through a 'refresh', for example the user typing a URL into the browser. In that case we will go straight to the server and not get a chance to run any authentication checks. The server will test that the user is authorised and if so return the content, at that point the app wants to display that content unless the user is not authenticated. The server will generate a user cookie if the user is authenticated and that will be stored permanently in the browser (the code does not handle expiry yet). If this cookie exists I assume the user is authenticated and set the 'authenticated' flag.

I wanted the authentication to work with routing, this means configuring the route provider.

var module = angular.module(appName, ['directives', 'localization', 'ngCookies']);

module.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
    $locationProvider.html5Mode(true);
    $routeProvider.
        when('/', {
            templateUrl: 'partials/index.html',
            controller: 'IndexCtrl',
            authenticate: true
        }).
        when('/login', {
            templateUrl: 'partials/login.html',
            authenticate: false
        }).
        when('/logout', {
            redirectTo: '/login',
            controller: 'LogoutCtrl',
            authenticate: false
        }).
        otherwise({
            redirectTo: '/'
        });
}]);

module.run(['$rootScope', '$location', 'authenticate', function ($rootScope, $location, authenticate) {
    $rootScope.$on("$routeChangeStart", function (event, next, current) {
        if((typeof(next.$route.authenticate) === "undefined" || next.$route.authenticate) 
            && !authenticate.isAuthenticated){
            $location.path( "/login" );
        }
    })
}]);

Firstly the routes. The routes are configured to have an 'authenticate' field which tells the code whether this route is protected, notice that login and logout are set to false otherwise nobody could ever login. The only other route is / and you have to be authenticated to go here. You can extend this scheme to do role based authentication as is shown here which is where I got this idea from.

The other key part of this is the handling of the route changed event. The code handles the $routeChangeStart event and checks two things: does the route require authentication and is the user currently authenticated? This is where Angular's singletons are very useful, the authentication service is a singleton, the isAuthenticated value will either have been set when we signed on, or it will have been set because $cookies.user existed. This means that if a route requires authentication and we are not authenticated then we set the browser location to the login page and get the user to login, otherwise we go to the actual route. Notice that the default is to require authentication, so if the authenticate flag is missing it's assumed to be true.

Finally the server. I've basically left the default passport code alone apart from adding two things. On successful authentication I add a cookie, and I remove the cookie when the user signs out.

app.post('/authenticate/local', function (req, res, next) {
    passport.authenticate('local', function (err, user, info) {
        if (err) {
            return next(err);
        }
        if (!user) {
            return res.send(401);
        }

        req.logIn(user, function (err) {
            if (err) {
                return next(err);
            }
            res.cookie('user', JSON.stringify({'id': user.id}), { httpOnly: false } );
            return res.send(200);
        });
    })(req, res, next);

});

app.get('/logout', function(req, res){
    req.logout();
    res.clearCookie("user");
    res.redirect('/');
});

Again, there's no explicit expiry on this yet, that's next

Wednesday 17 July 2013

Built in Angular Directives

If you do a search for Angular directives most of the hits you get will be on how to write your own. Of course at some point you will want to do this. However Angular has many builtin directives ready for us to use.

So what are directives? The docs have this to say

Angular comes with a built in set of directives which are useful for building web applications but can be extended such that HTML can be turned into a declarative domain specific language (DSL).

If you look at the Angular docs on directives you'll see that it mostly talks about writing directives and the mechanics of how directives work. However if you look at the API docs you'll see many directives listed that are part of the framework. What I think is surprising about this set of directives is that as well as the ng* set of directives that augment the DOM, such as ngRepeat and ngClick, there are directives that replace standard HTML elements such as 'a' and 'input'. Obviously Angular is doing a lot of work under the covers to make our lives easier.

Thursday 20 June 2013

Angular JS - Almost the simplest thing you can do

While Angular lets you build very complex applications it also lets you do very simple things, the simplest of which is to bind data to a model. You do this by using templates. Templates allow you to output data, carry out calculations in-line. Taken from the AngularJS docs:

  • Attribute Evaluation: evaluation of all properties are against the scope, doing the evaluation, unlike in JavaScript where the expressions are evaluated against the global window.

  • Forgiving: expression evaluation is forgiving to undefined and null, unlike in JavaScript, where trying to evaluate undefined properties can generate ReferenceError or TypeError.

  • No Control Flow Statements: you cannot do any of the following in angular expression: conditionals, loops, or throw.

  • Filters: you can pass result of expression evaluations through filter chains. For example to convert date object into a local specific human-readable format.

Templates are simple to use, like this:
{{3 + 3}}

However this is not particularly interesting in and off itself, we are simply displaying data. To make this both more interesting and useful we'd like ability to dynamically grab dats from the user and then update the page using that data, this is where two-way binding comes in.

Two way data binding can be added to elements on the page. This binding allows data to be entered into one element and the value of that data to be reflected back through other elements on the page. To do this an input element is annotated with an ng-model attribute. This attribute specifies the name of the 'model' that the data will be stored in. As data is entered into the element Angular will update the model with this data. The value of this model can then be displayed using the 'moustache' syntax in a template as seen above.

{{3 + 34}}

Hello {{name}}

If a user enters the value 'Kevin' into the input field then the following is produced:

Simpledatabinding

The key thing here is hg-model, this creates a model called 'name' which is then available to be used in the rest of the page, in this case inside the h2 tag. ng-model (aka ngModel) takes a string value which specifies the name of the model that Angular binds the data to.

Data-binding is a general purpose mechanism. This means that you can bind the data to other places in the DOM such as a class name or a attribute value.

Batarang (Debugging AngularJS)

Came across this today Needs Chrome Canary builds but it looks very cool