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

Wednesday 19 June 2013

ngApp in AngularJs

AngularJS applications need to be 'bootstrapped.' This is done by adding the ng-app directive to an element in the page. You can use any element but typically the root 'body' or 'html' element is used.

<html ng-app="">
</html>

Notice that hg-app takes a string value, this is the name of a module to load, if you have no modules this can be the empty string.

Once you have this in place you can then start using Angular

<html ng-app="">
<body>

{{3 + 34}}
</body> </html>

will cause angular to 'execute' the template and display the value 37 on the page.

This is about as simple as it gets for Angular code.

The problem with the simple approach of just using ng-app is that this is not compliant with the HTML 5 specification. Because of this Angular provides other ways of defining directives. Instead of just ng-app, you could use data-ng-app (this is the most widely used alternative) x-ng-app. This applies to all the directives not just ngApp.

Sunday 16 June 2013

Installing Node and MongoDB On Mac OSX

There are many ways to install Node and MongoDB on OSX, the easiest way is to use a package management tool, and I prefer to use Brew.

Before you install Brew you will need to install XCode (you'll need the compiler)

If you don't have brew installed you can follow the instructions here On Mac: install brew, or simply type
ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
into a shell.
Once you have that you can then do:
brew update
then
brew install node

brew install mongodb

to install NodeJs and MongoDB respectively