Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Monday, May 30, 2016

Lazy inter-module communication with require() and webpack

Motivation

CommonJS' modules can be loaded on demand with require.ensure(...). Example:
require.ensure(["module-a"], function(require) {
    var a = require("module-a");
    // module-a can be used now ...
});
The well-known module bundler Webpack supports this syntax for loading modules on demand. Such lazy loaded module(s) define a split point. Split points split the codebase into "chunks" (separate JavaScript files) which are loaded on demand. This feature allows to keep the initial download small and only download the code when it's requested.

Webpack performs a static analysis of the code at the project build time. Dynamic information is only available at runtime and not available at build time. You have to define your modules as constants. This leads to the problem. The following code will not work:
var modA = "module-a";
require.ensure([modA], function(require) {
    var a = require("module-a");
    // module-a can be used now ...
});
Webpack says something like: Error: Module not found. A dependency to an entry point is not allowed.

Theoretically you can use require.context, but this needs some parts of module's path as constants too. Now, imagine you develop a modular UI library and would like to provide some callbacks for certain events (event handlers). Users of your library use it as a third-party library. They want to register their custom callback functions from their own modules. And they want to be called lazy, on demand, when an event occurs. You have many users and your library doesn't know about custom modules of course. That means, you can not hard-code module's path and function's name a priori. What to do?

Solution

This POC on GitHub demonstrates how to use truly dynamic require with webpack. It simulates third-party and own modules. The project structure:
index.html          // some HTML template from the third-party UI library 
external-modules    // third-party library with CommonJS modules
own-modules         // some library with CommonJS modules which uses the third-party library
common-modules      // simple utility modules
The modules in external-modules don't know about own-modules. Modules in the own-modules import modules from the external-modules. The most important webpack loader we use in this POC is bundle loader for webpack. This loader creates a special function with required module which can be executed lazily. The required module gets loaded when the function is executed.

The HTML template in the POC, backed by the third-party (external) modules, look like as follows (only short snippet):
<button data-init="button" data-onclick='["ownModules.button", "alertSync"]'>
    Try sync. call (ok)
</button>
<p></p>
<button data-init="button" data-onclick='["ownModules.button", "alertAsyncValid"]'>
    Try async. call (ok)
</button>
<p></p>
<button data-init="button" data-onclick='["ownModules.button", "alertAsyncInvalid"]'>
    Try async. call (error)
</button>
The full code can be found in index.xhtml. The callback functions are defined as values of data-* attributes. Example: data-onclick='["ownModules.button", "alertSync"]'. Here, the function alertSync from the namespace ownModules.button wants to be called when the button is clicked. The namespace is a simple JavaScript object defined in the global window scope.

The module's functions in the folder own-modules returns Promises. Promises allow asynchronous callbacks. In the POC, the NPM module promise light is used. If you have jQuery in your web app, you can use jQuery Deferred Object as well because it provides the same functionality. The sample implementation of the button.js looks like as
var Promise = require('promise-light');

module.exports = {
    alertSync: function (element) {
        document.getElementById('output').innerHTML = 'Clicked button. Sync. call is OK';
        return Promise.resolve('OK');
    },
    
    alertAsyncValid: function (element) {
        return new Promise(function setup(resolve, reject) {
            setTimeout(function () {
                document.getElementById('output').innerHTML = 'Clicked button. Async. call is OK';
                resolve('OK');
            }, 1000);
        });
    },
    
    alertAsyncInvalid: function (element) {
        return new Promise(function setup(resolve, reject) {
            setTimeout(function () {
                document.getElementById('output').innerHTML = 'Clicked button. Async. call has ERROR';
                reject('ERROR');
            }, 1000);
        });
    }
};
The external modules should be bootstrapped by the own modules. The bootstrapping occurs in the file entry.js which acts as an entry point in the webpack configuration.
var objectifier = require("./../common-modules/objectifier");
var bootstrap = require("./../external-modules/bootstrap");

document.addEventListener("DOMContentLoaded", function (event) {    
    // initialize external modules
    bootstrap.bootstrap();
    
    // lazy load own modules (s. https://github.com/webpack/bundle-loader)
    var lazyLoadedButton = require("bundle?lazy!./button");
    var lazyLoadedCheckbox = require("bundle?lazy!./checkbox");
    
    // and put them under namespaces ownModules.button and ownModules.checkbox resp. 
    objectifier.set('ownModules.button', lazyLoadedButton);
    objectifier.set('ownModules.checkbox', lazyLoadedCheckbox);
});
As you can see, the bundle loader is applied to the own modules. The returned functions are saved under arbitrary namespaces in order to be executed later in external modules. Note: the objectifier is just a CommonJS module porting of the beautiful getter and setter implementation for nested objects. The button.js in the external-modules registers an onlick event handler for all HTML elements having the data-init="button" attribute.
var lazyloader = require("./lazyloader");

module.exports = {
    init: function () {
        var elements = document.querySelectorAll('[data-init="button"]');
        for (var i = 0; i < elements.length; i++) {
            (function () {
                var element = elements[i];

                // onlick event handler
                var onclick = function () {
                    // so something module specific ...

                    // execute function defined via data-onclick attribute if it's defined,
                    // e.g. data-onclick='["ownModules.button", "alertSync"]'
                    lazyloader.execute(element, 'data-onclick',
                        function resolved(value) {
                            alert(value);
                        }, function rejected(error) {
                            alert(error);
                        });
                };

                element.addEventListener("click", onclick);
            })();
        }

        return elements;
    },

    doSomething: function () {
        // ...
    }
};
The core logic for the event handling is encapsulated in the lazyloader.js.
/**
 Loads modules specified via data-* attributes and executes the specified function.
 HTML-Example:

 <button data-init="button" data-onclick='["ownModules.button", "alertSync"]'>
    ...
 </button>

 The function ownModules.button.alertSync(button) will be executed.
 */

var objectifier = require("./../common-modules/objectifier");

module.exports = {
    /**
     * Executes the specified synchronous or asynchronous function from the specified module
     * and invokes resolved or rejected callbacks respectively.
     * The function should return a promise.
     * 
     * @param element HTML element where data-* is defined
     * @param data value of data-* as string
     * @param resolvedCallback callback which gets executed when the returned promise is fullfilled
     * @param rejectedCallback callback which gets executed when the returned promise is rejected
     */
    execute: function (element, data, resolvedCallback, rejectedCallback) {
        var strData = element.getAttribute(data);
        if (strData) {
            var objData = JSON.parse(strData);
            var module = objData[0];
            var func = objData[1];

            if (module && objectifier.exists(module) && func) {
                // module and function exist ==> load the module
                objectifier.get(module)(function (module) {
                    // execute the specified function from the module.
                    // return value is a promise (see https://www.npmjs.com/package/promise-light)
                    module[func](element).then(
                        function resolved(value) {
                            resolvedCallback(value);
                        },
                        function rejected(error) {
                            rejectedCallback(error);
                        });
                });
            }
        }
    }
};
It executes the specified function from the specified module. The caller receives a promise. If the promise gets resolved, the first function in .then(...) is executed. If the promise gets rejected, the second function in .then(...) is executed. The synchronous or asynchronous function from the own modules can control whether it resolves or rejects the promise. The caller can decide then what it should do in both cases. For instance, assume you got an accordion widget which belongs to a third-party library. One interesting case could be the validation of the content of an open accordion tab when the tab's header is clicked by the user. A custom function could validate the tab content on such click. In case of a successful validation, the clicked tab can be closed. Otherwise, the tab should stay open.

Screens

The initial load doesn't fetch chunks from the own modules.

Image

Click on a button triggers the load of the first chunk.

Image

Click on a checkbox triggers the load of the second chunk.

Image

Sunday, March 20, 2016

Promises in AngularJS. Part II. $q service.

I have already blogged about Promises in AngularJS 1.x. This is the second part which describes the Angular's $q service. The $q service can be used in two different ways. The first way mimics the Q library for creating and composing asynchronous promises in JavaScript. The second way mimics the ECMAScript 2015 (ES6) style. Let's begin with the first way. First of all, you have to create a deferred object by $q.defer().
 
var deferred = $q.defer();
 
A deferred object can be created within an asynchronous function. The function should return a promise object created from the deferred object as follows:
 
return deferred.promise;
 
A promise is always in either one of three states:
  1. Pending: the result hasn't been computed yet
  2. Fulfilled: the result was computed successfully
  3. Rejected: a failure occurred during computation
When the asynchronous function finished the execution, it can invoke one of the two methods:
deferred.resolve(...)
deferred.reject(...)
The first call deferred.resolve(...) puts the promise into the fulfilled state. As result a success callback will be invoked. The second call deferred.reject(...) puts the promise into the rejected state. As result an error callback will be invoked. It is also possible to invoke
 
deferred.notify(...)
 
during the function's execution to propogate some progress from the asynchronous function to an update callback. All three callbacks can be registered on the promise as parameters of the function then:
var promise = someAsynchronousFunction();
promise.then(function(value) {
    // success
    ...
}, function(reason) {
    // failure
    ...
}, function(update) {
    // update
    ...
});
Let's implement an example. We will take setTimeout() as an asynchronous function. In the real application, you will probably use some other asynchronous services. In the setTimeout(), we will generate a random number after 1 sek. If the number is less than 0.5, we will invoke deferred.resolve(random), otherwise deferred.reject(random). The entire logic is implemented in the controller PromiseController.
var app = angular.module('app', []);
app.controller('PromiseController', PromiseController);

function PromiseController($q) {
    var _self = this;
    this.message = null;

    var asyncFunction = function() {
        var deferred = $q.defer();

        setTimeout(function() {
            var random = Math.random().toFixed(2);

            if (random < 0.5) {
                deferred.resolve(random);
            } else {
                deferred.reject(random);
            }
        }, 1000);

        return deferred.promise;
    }

    this.invokeAsyncFunction = function() {
        var promise = asyncFunction();

        promise.then(function(message) {
            _self.message = "Success: " + message;
        }, function(message) {
            _self.message = "Error: " + message;
        });
    }
}
As you can see, the asynchronous function asyncFunction is invoked in the controller's method invokeAsyncFunction. The function asyncFunction returns a promise. In the success case, the promise gets fulfilled and the first registered success callback gets executed. In the error case, the promise gets rejected and the second registered error callback gets executed. The invokeAsyncFunction is bound to the onclick event on a button.
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta content="IE=edge" http-equiv="X-UA-Compatible" />
    <script src="https://code.angularjs.org/1.4.8/angular.js"></script>
</head>

<body ng-app="app" ng-controller="PromiseController as ctrlPromise">
    <div ng-bind="ctrlPromise.message"></div>
    <p></p>
    <button ng-click="ctrlPromise.invokeAsyncFunction()">
        Invoke asynchronous function
    </button>

    <script src="controller.js"></script>
</body>
</html>
The message in GUI looks like as follows (success case as example):

Image

The Plunker of this example is available here. Here is another picture that visualize the relation between methods of the deferred object:

Image

As you can see, the asynchronous function doesn't return the deferred object directly. The reason for that is obvious. If the deferred object would be returned instead of deferred.promise, the caller of the asynchronous function could be able to trigger callbacks by invoking deferred.resolve(...), deferred.reject(...) or deferred.notify(...). For this reason these methods are protected from being invoking from outside by the caller.

The example above can be rewritten in the ECMAScript 2015 (ES6) style (I mentioned this way at the beginning). A promise in ECMAScript 2015 can be created as an instance of Promise object.
var promise = new Promise(function(resolve, reject) {
    ...
    if(...) {
        resolve(value); // success
    } else {
        reject(reason); // failure
    }
});
Our asyncFunction function looks in this case as follows:
var asyncFunction = function() {
    return $q(function(resolve, reject) {

        setTimeout(function() {
            var random = Math.random().toFixed(2);

            if (random < 0.5) {
                resolve(random);
            } else {
                reject(random);
            }
        }, 1000);
    });
}
The remaining code stays unchanged. Let's go on. The next question is, how can we produce a rejection in success or error callbacks? For instance, you check some condition in a callback and want to produce an error if the condition is not fulfilled. Sometimes, we also want to forward rejection in a chain of promises. That means, you catch an error via an error callback and you want to forward the error to the promise derived from the current promise. There are two ways to achieve this qoal. The first one consists in using the $q.reject(...) like shown below.
var promise = someAsynchronousFunction();
promise.then(function(value) {
    // success
    ...
    if(someCondition) {
        $q.reject("An error occurred!");
    }
}, function(reason) {
    // failure
    ...
}).catch(function(error) {
    // do something in error case
    ...
});
In our example, we will adjust the function invokeAsyncFunction in order to check very small values (smaller than 0.1).
this.invokeAsyncFunction = function() {
    var promise = asyncFunction();

    promise.then(function(random) {
        if (random < 0.1) {
            return $q.reject("Very small random value!");
        }

        _self.message = "Success: " + random;
    }, function(random) {
        _self.message = "Error: " + random;
    }).catch(function(error) {
        _self.message = "Special error: " + error;
    });
}

Image

A Plunker example is available here. Keep in mind the difference between deferred.reject(...) and $q.reject(...). The call deferred.reject(...) puts the corresponding promise into the rejected state. The call $q.reject(...) creates a new promise which is already in the rejected state.

The second way to produce a rejection in success or error callbacks consists in throwing an exception with throw new Error(...).
this.invokeAsyncFunction = function() {
    var promise = asyncFunction();

    promise.then(function(random) {
        if (random < 0.1) {
            throw new Error("Very small random value!");
        }

        _self.message = "Success: " + random;
    }, function(random) {
        _self.message = "Error: " + random;
    }).catch(function(error) {
        _self.message = "Special error: " + error;
    });
}
AngularJS will catch the exception, create a promise in the rejected state and forward it to the next block in the chain of promises. In the example, the error will be forwarded to the catch block. One downside of this approach with throw new Error(...) is that the error will be logged in the console. Picture from the Chrome Dev Tools:

Image

But well, it works as designed and probably it is even an advantage to see thrown errors in the console.

There is also an opposite method $q.when(...) which returns an immediately resolved promise. The documentation says: $q.when(...) "wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted." You can e.g. wrap an jQuery Deferred Object with $q.when(...) or simple write
$q.when("Finished!").then(
    function handleResolve(value) {
        console.log("Resolved with value: ", value);
    }
);
and see Resolved with value: Finished! in the console. The alias of $q.when(value) is $q.resolve(value). This was introduced later in order to maintain naming consistency with ECMAScript 2015.

Last but not least is the method $q.all(promises) where promises is an array of multiple promises. This call returns a single promise that is resolved when all promises in the given array gets resolved.
var promise1 = someAsynchronousFunction1();
var promise2 = someAsynchronousFunction2();

$q.all([promise1, promise2]).then(function(result) {
    console.log("Promises " + result[0] + " and " + result[1] + " finished their work successfully");
});
As you can see, the result passed into the callback function is an array of two outcomes - the outcome of the first and the outcome of the second callback.

Sunday, January 3, 2016

Installing Babel command line and playing with ES6

If you would like to play with ECMAScript6, you can install Babel CLI globally on your machine and run the babel-node command. babel-node is a version of the Node.js executable that understands ES6. It can be installed with the package manager NPM. Installation instructions are listed below.

1) Install two Node.js modules babel and babel-cli one after another. Note: if you have some troubles during installation, update your Node.js and NPM to the latest stable versions.

npm install --global babel
npm install --global babel-cli

2) Install a set of plugins for available ES6 (ES2015) features. Without these plugins you will get errors about unexpected token or similar. See http://babeljs.io/docs/plugins/preset-es2015/ for more details about supported ES6 features.

npm install --global babel-preset-es2015

3a) Start interactive REPL and type ES6 code. Note: globally installed modules on Windows are located under C:\Users\<Username>\AppData\Roaming\npm\node_modules, so that we need to put a complete path to them.

babel-node --presets C:\\Users\\Oleg\\AppData\\Roaming\\npm\\node_modules\\babel-preset-es2015
> let arr = [1,2,3]
> arr.map(x => x * x)
Output: [1,4,9]


3b) Alternative, you can place some ES6 code into a file, e.g. testes6.js, and let it run.

babel-node --presets C:\\Users\\Oleg\\AppData\\Roaming\\npm\\node_modules\\babel-preset-es2015 testes6.js
Output: [1,4,9]


In the example, babel-node is executed in the same directory where the file testes6.js is located.

Enjoy.

Wednesday, December 30, 2015

The best way for sharing data between controllers in AngularJS 1.x

You may know the situation in AngularJS 1.x when multiple independent controllers need to share some data. E.g. one controller adds some data that should be available in the other controllers in the same view. So far as I know there are three possibilities:
  • Using $scope, e.g. $scope of a common parent controller or $rootScope
  • Using publish-subscribe design pattern via $emit / $broadcast (fire events) and $on (listen for events)
  • Using services which can be injected in multiple controllers.
The first possibility to share data via $scope is a bad idea. If you share data via scopes, you have to know controllers' parent-child relationship. That means, your controllers are tightly coupled and the refactoring is hard to master. Some AngularJS examples save the data on the $rootScope, but the pollution of $rootScope is not recommended. Keep the $rootScope as small as possible. The event based possibility to share data via $emit, $broadcast and $on is a better approach in comparison to scope based one. The controllers are loosely coupled. But there is a disadvantage as well - the performance. The performance is ok for a small application, but for a large application with hundreds of $on listeners, the AngularJS has to introspect all scopes in order to find the $on listeners that fit to the corresponsing $emit / $broadcast. From the architecture point there is a shortcoming as well - we still need scopes to register $emit, $broadcast and $on. Some people also say - communication details should be hidden for the same reason we keep $http hidden behind a service layer. There are 5 guidelines for avoiding scope soup in AngularJS. The last 5. rule is called Don't Use Scope To Pass Data Around. It advices against using scopes directly and against event based approach. Last but not least - think about the upcomming AngularJS 2. It doesn't have scopes!

In my opinion, the preferred way for sharing data between controllers in AngularJS 1.x is the third possibility. We can use services. A service can keep data or acts as event emitter (example is shown below). Any service can be injected into controllers, so that conrollers still don't know from each other and thus are loosely coupled. I will refactor and extend one example from this blog post. The author of this blog post implemented two controllers for two panes. In the left pane we can input some text and add it as an item to a list in the right pane. The list itself is placed in a service (service encapsulates the data). This is probably a good idea when you have different views or controllers in conditional ng-if. But if controllers exist in the same view and show their data at the same time, the list should reside in the controller for the right pane and not in the service. The list belongs to the second controller. This is my opinion, so I will move the list into the controller and also add a third "message controller" which is responsible for messages when user adds items to the list or removes them from the list. We thus have three controllers and one service. The idea is to apply the listener pattern (also known as observer) to the service. The service acts as event emitter. Every controller can fire an ADD or REMOVE operation and register a listener function to be notified when the operation is done. The added / removed item acts as data passed along with operation into all registered listeners for the given operation. The live example is implemented on Plunker.

The first picture shows the adding process (click on the button Add To List) with a message about the successfully added item.

Image

The second picture shows the removing process (click on a link with x symbol) with a message about the successfully removed item.

Image

The HTML part looks as follows:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta content="IE=edge" http-equiv="X-UA-Compatible" />
    <script src="https://code.angularjs.org/1.4.8/angular.js"></script>
</head>

<body ng-app="app">
    <div ng-controller="MessageController as ctrlMessage" style="margin-bottom:20px;">
        <span ng-bind-html="ctrlMessage.message"></span>
    </div>

    <div ng-controller="PaneOneController as ctrlPaneOne">
        <input ng-model="ctrlPaneOne.item">
        <button ng-click="ctrlPaneOne.addItem(ctrlPaneOne.item)">Add To List</button>
    </div>

    <div ng-controller="PaneTwoController as ctrlPaneTwo" style="float:right; width:50%; margin-top:-20px;">
        <ul style="margin-top:0">
            <li ng-repeat="item in ctrlPaneTwo.list track by $index">
                {{item}}
                <a href style="margin-left:5px;" title="Remove" ng-click="ctrlPaneTwo.removeItem($index)">x</a>
            </li>
        </ul>
    </div>

    <script src="controller.js"></script>
    <script src="service.js"></script>
</body>
</html>
As you can see, there are three independent HTML div elements with ng-controller directive. The MessageController shows a message above. The PaneOneController keeps an input value and the function addItem(). The PaneTwoController keeps a list with all items and the function removeItem(). The controllers look as follows in details:
(function() {
    var app = angular.module('app', []);
    app.controller('PaneOneController', PaneOneController);
    app.controller('PaneTwoController', PaneTwoController);
    app.controller('MessageController', MessageController);

    /* first controller */
    function PaneOneController(EventEmitterListService) {
        var _self = this;
        this.item = null;

        this.addItem = function(item) {
            EventEmitterListService.emitAddItem(item);
            _self.item = null;
        }
    }

    /* second controller */
    function PaneTwoController($scope, EventEmitterListService) {
        var _self = this;
        this.list = [];

        this.removeItem = function(index) {
            var removed = _self.list.splice(index, 1);
            EventEmitterListService.emitRemoveItem(removed[0]);
        }

        EventEmitterListService.onAddItem('PaneTwo', function(item) {
            _self.list.push(item);
        });

        $scope.$on("$destroy", function() {
            EventEmitterListService.clear('PaneTwo');
        });
    }

    /* third controller */
    function MessageController($scope, $sce, EventEmitterListService) {
        var _self = this;
        this.message = null;

        EventEmitterListService.onAddItem('Message', function(item) {
            _self.message = $sce.trustAsHtml("<strong>" + item + "</strong> has been added successfully");
        });

        EventEmitterListService.onRemoveItem('Message', function(item) {
            _self.message = $sce.trustAsHtml("<strong>" + item + "</strong> has been removed successfully");
        });

        $scope.$on("$destroy", function() {
            EventEmitterListService.clear('Message');
        });
    }
})();
All three controllers communicate with a service called EventEmitterListService. The service exposes three methods:
  • emitAddItem - notifies listeners that are interested in adding an item to the list. The item is passed as parameter.
  • emitRemoveItem - notifies listeners that are interested in removing an item from the list. The item is passed as parameter.
  • onAddItem - registers a listener function that is interested in adding an item to the list. The listener is passed as parameter.
  • onRemoveItem - registers a listener function that is interested in removing an item from the list. The listener is passed as parameter.
  • clear - removes all registered listeners which belong to the specified controller. The controller is identified by the scope parameter (simple unique string).
The clear method is important when the $scope gets destroyed (e.g. when the DOM associated with the $scope gets removed due to ng-if or view switching). This method should be invoked on $destroy event - see code snippets with $scope.$on("$destroy", function() {...}). The full code of the service is listed below:
(function() {
    var app = angular.module('app');
    app.factory('EventEmitterListService', EventEmitterListService);

    function EventEmitterListService() {
        // Format of any object in the array:
        // {scope: ..., add: [...], remove: [...]}
        // "scope": some identifier, e.g. it can be the part of controller's name
        // "add": array of listeners for the given scope to be notified when an item is added
        // "remove": array of listeners for the given scope to be notified when an item is removed
        var listeners = [];

        function emitAddItem(item) {
            emitAction('add', item);
        }

        function onAddItem(scope, listener) {
            onAction('add', scope, listener);
        }

        function emitRemoveItem(item) {
            emitAction('remove', item);
        }

        function onRemoveItem(scope, listener) {
            onAction('remove', scope, listener);
        }

        function clear(scope) {
            var index = findIndex(scope);
            if (index > -1) {
                listeners.splice(index, 1);
            }
        }

        function emitAction(action, item) {
            listeners.forEach(function(obj) {
                obj[action].forEach(function(listener) {
                    listener(item);
                });
            });
        }

        function onAction(action, scope, listener) {
            var index = findIndex(scope);
            if (index > -1) {
                listeners[index][action].push(listener);
            } else {
                var obj = {
                    'scope': scope,
                    'add': action == 'add' ? [listener] : [],
                    'remove': action == 'remove' ? [listener] : []
                }
                listeners.push(obj);
            }
        }

        function findIndex(scope) {
            var index = -1;
            for (var i = 0; i < listeners.length; i++) {
                if (listeners[i].scope == scope) {
                    index = i;
                    break;
                }
            }

            return index;
        }

        var service = {
            emitAddItem: emitAddItem,
            onAddItem: onAddItem,
            emitRemoveItem: emitRemoveItem,
            onRemoveItem: onRemoveItem,
            clear: clear
        };

        return service;
    }
})();
That's all. Happy New Year!

Wednesday, December 23, 2015

Mock responses to HTTP calls with network traffic simulation by using ngMockE2E

The AngularJS' module ngMockE2E allows to fake HTTP backend implementation for unit testing and to respond with static or dynamic responses via the when API and its shortcuts (whenGET, whenPOST, etc). In this post, I will only demonstrate how to respond to any HTTP calls by mocking the responses. We will also see how the network load can be simulated. Such behavior gives a feeling of real remote calls. The implemented example is available on Plunker. It shows a small CRUD application to manage imaginary persons. There are four buttons for sending AJAX requests in a REST like manner. When a request is sent, you can see the text Loading... which disappears after 2 seconds when the response "is arrived". In fact, no request is sent of course. This is just a simulation with the AngularJS' module ngMockE2E. The network load takes exactly 2 seconds but you can set another delay value if you want

Image

The first button sends a GET request to /persons to receives all available persons. If you click on this button, you will see three persons received from the imaginary backend.

Image

The second button sends a GET request to /person/:id to receives one person by its ID. The ID is appended to the URI part /person/. If you type e.g. 2 and click on this button, you should see the following person

Image

There is also a proper validation. If you type e.g. 4, no person will be found because no person with the ID 4 exists in "the backend".

Image

If you type some wrong ID which is not a numeric value, an error message will be shown as well.

Image

The third button sends a POST request to /person to create a new person or update existing one. The input fields name and birthday are required. You can type a name and birthday for a new person, sends the POST request and see the created person recived from "the backend".

Image

Now, if you sends a GET request to /persons (the first button), you should see the created person in the list of all persons.

Image

The last button deletes a person by sending the DELETE request to /person/:id. The deleted person is shown above the buttons.

Image

Click on the first button again to ensure that the person was deleted and doesn't exist more.

Image

Let's show the code. First of all you have to include the file angular-mocks.js after the file angular.js in order to overwrite the original $httpBackend functionality.
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular-mocks.js"></script>
What is $httpBackend? In AngularJS, we normally use a high level $http or $resource service for HTTP calls. These services use for their part a low level service called $httpBackend. The $httpBackend has useful methods to create new backend definitions for various request types. There is a method when(method, url, [data], [headers], [keys]) and many shortcut methods such as
  • whenGET(url, [headers], [keys])
  • whenHEAD(url, [headers], [keys])
  • whenDELETE(url, [headers], [keys])
  • whenPOST(url, [data], [headers], [keys])
  • whenPUT(url, [data], [headers], [keys])
  • ...
The most interesting parameter is the url. This can be a String like /persons, a regular expression like /^\/person\/([0-9]+)$/ for /person/:id or a function that receives the url and returns true if the url matches the current definition. These methods return an object with respond and passThrough functions that control how a matched request is handled. The passThrough is useful when the backend REST API is ready to use and you want to pass mocked request to the real HTTP call. In this example, we only use the respond. The respond can take the object to be returned, e.g. $httpBackend.whenGET('/persons').respond(persons) or a function function(method, url, data, headers, params) which returns an array containing response status (number), response data (string), response headers (object), and the text for the status (string). The file app.js demonstrates how to use the $httpBackend.
(function() {
    var app = angular.module('app', ["ngMockE2E"]);
    app.run(HttpBackendMocker);

    // original list of persons
    var persons = [
      {id: 1, name: 'Max Mustermann', birthdate: '01.01.1970'},
      {id: 2, name: 'Sara Smidth', birthdate: '31.12.1982'},
      {id: 3, name: 'James Bond', birthdate: '05.05.1960'}
    ];

    // Reg. expression for /person/:id
    var regexPersonId = /^\/person\/([0-9]+)$/;

    function HttpBackendMocker($httpBackend) {
        // GET /persons
        $httpBackend.whenGET('/persons').respond(persons);

        // GET /person/:id
        $httpBackend.whenGET(regexPersonId).respond(function(method, url) {
            var id = url.match(regexPersonId)[1];
            var foundPerson = findPerson(id);

            return foundPerson ? [200, foundPerson] : [404, 'Person not found'];
        });

        // POST /person
        $httpBackend.whenPOST('/person').respond(function(method, url, data) {
            var newPerson = angular.fromJson(data);
            // does the person already exist?
            var existingPerson = findPerson(newPerson.id);

            if (existingPerson) {
                // update existing person
                angular.extend(existingPerson, newPerson);
                return [200, existingPerson];
            } else {
                // create a new person
                newPerson.id = persons.length > 0 ? persons[persons.length - 1].id + 1 : 1;
                persons.push(newPerson);
                return [200, newPerson];
            }
        });

        // DELETE: /person/:id
        $httpBackend.whenDELETE(regexPersonId).respond(function(method, url) {
            var id = url.match(regexPersonId)[1];
            var foundPerson = findPerson(id);

            if (foundPerson) {
                persons.splice(foundPerson.id - 1, 1);
                // re-set ids
                for (var i = 0; i < persons.length; i++) {
                    persons[i].id = i + 1;
                }
            }

            return foundPerson ? [200, foundPerson] : [404, 'Person not found'];
        });

        // helper function to find a person by id
        function findPerson(id) {
            var foundPerson = null;
            for (var i = 0; i < persons.length; i++) {
                var person = persons[i];
                if (person.id == id) {
                    foundPerson = person;
                    break;
                }
            }

            return foundPerson;
        }
    }
})();
Now we need a custom service that encapsulates the $http service. The service will get the name DataService. It is placed in the file service.js.
(function() {
    var app = angular.module('app');
    app.factory('DataService', DataService);

    function DataService($http) {
        var service = {
            getPersons: getPersons,
            getPerson: getPerson,
            addPerson: addPerson,
            removePerson: removePerson
        };

        return service;

        function getPersons() {
            return $http.get('/persons').then(
                function(response) {
                    return response.data;
                },
                function(error) {
                    // do something in failure case
                }
            );
        }

        function getPerson(id) {
            return $http.get('/person/' + id).then(
                function(response) {
                    return response.data;
                },
                function(error) {
                    if (error.status && error.status === 404) {
                        return error.data;
                    } else {
                        return "Unexpected request";
                    }
                }
            );
        }

        function addPerson(person) {
            return $http.post('/person', person).then(
                function(response) {
                    return response.data;
                },
                function(error) {
                    // do something in failure case
                }
            );
        }

        function removePerson(id) {
            return $http.delete('/person/' + id).then(
                function(response) {
                    return response.data;
                },
                function(error) {
                    if (error.status && error.status === 404) {
                        return error.data;
                    } else {
                        return "Unexpected request";
                    }
                }
            );
        }
    }
})();
The service DataService is invoked by a controller which I named DataController and placed in the controller.js.
(function() {
    var app = angular.module('app');
    app.controller('DataController', DataController);

    function DataController($scope, DataService) {
        var _self = this;
        this.persons = [];
        this.personId = null;
        this.person = {};
        this.message = null;
        this.loading = false;

        this.getPersons = function() {
            init();

            DataService.getPersons().then(function(data) {
                _self.persons = data;
                _self.loading = false;
            })
        }

        this.getPerson = function(id) {
            // check required input
            if ($scope.form.id4get.$error.required) {
                _self.message = "Please add person's id";
                return;
            }

            init();

            DataService.getPerson(id).then(function(data) {
                if (typeof data === "string") {
                    // error
                    _self.message = data;
                    _self.persons = null;
                } else {
                    _self.persons = [data];
                }

                _self.loading = false;
            })
        }

        this.addPerson = function(person) {
            // check required input
            if ($scope.form.name.$error.required) {
                _self.message = "Please add person's name";
                return;
            }
            if ($scope.form.birthdate.$error.required) {
                _self.message = "Please add person's birthdate";
                return;
            }

            init();

            DataService.addPerson(person).then(function(data) {
                _self.persons = [data];
                _self.loading = false;
            })
        }

        this.removePerson = function(id) {
            // check required input
            if ($scope.form.id4delete.$error.required) {
                _self.message = "Please add person's id";
                return;
            }

            init();

            DataService.removePerson(id).then(function(data) {
                if (typeof data === "string") {
                    // error
                    _self.message = data;
                    _self.persons = null;
                } else {
                    _self.persons = [data];
                }

                _self.loading = false;
            })
        }

        // helper function to reset internal state
        var init = function() {
            _self.persons = [];
            _self.message = null;
            _self.loading = true;
        }
    }
})();
Now we can use the controller in the view - the file index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta content="IE=edge" http-equiv="X-UA-Compatible" />
    <script src="https://code.angularjs.org/1.4.8/angular.js"></script>
    <script src="https://code.angularjs.org/1.4.8/angular-mocks.js"></script>
</head>
<body ng-app="app">
    <div ng-controller="DataController as ctrl" ng-cloak>
        <ul style="padding-left:0; list-style:none;">
            <li ng-repeat="person in ctrl.persons">
                {{person.id}} - {{person.name}} - {{person.birthdate}}
            </li>
        </ul>

        <div style="margin-bottom:15px;" ng-show="ctrl.loading">Loading ...</div>
        <div style="color:red; margin-bottom:15px;" ng-show="ctrl.message">{{ctrl.message}}</div>

        <form name="form" novalidate>
            <button ng-click="ctrl.getPersons()">GET /persons</button>
            <p></p>
            <button ng-click="ctrl.getPerson(ctrl.personId)">GET /person/:id</button>
            <input ng-model="ctrl.personId" name="id4get" required placeholder="id" />
            <p></p>
            <button ng-click="ctrl.addPerson(ctrl.person)">POST /person</button>
            <input ng-model="ctrl.person.name" name="name" required placeholder="name" />
            <input ng-model="ctrl.person.birthdate" name="birthdate" required placeholder="birthdate" />
            <p></p>
            <button ng-click="ctrl.removePerson(ctrl.personId)">DELETE /person/:id</button>
            <input ng-model="ctrl.personId" name="id4delete" required placeholder="id" />
        </form>
    </div>

    <script src="app.js"></script>
    <script src="config.js"></script>
    <script src="service.js"></script>
    <script src="controller.js"></script>
</body>
</html>
The current implementation has one shortcoming. The text Loading ... is not shown at all because the response is delivered very quickly, so that user doesn't see it. It would be nice to delay HTTP calls in order to simulate the network load. For this purpose we can use the $provide service and register a service decorator for the $httpBackend. The service decorator acts as proxy. If you look into the source code of the $httpBackend, you can see that the $httpBackend is created by the constructor function(method, url, data, callback, headers, timeout, withCredentials). The four parameter callback is responsible for the response. We have to provide our own implementation which I named delayedCallback. The delayedCallback function invokes the original callback with a delay (here 2 seconds) by means of setTimeout(). We delegate the proxy call to the $httpBackend instantiation, but with the new delayed callback. The injected $delegate object points exactly to the $httpBackend. The full code is shown below.
(function() {
    var app = angular.module('app');
    app.config(HttpBackendConfigurator);

    function HttpBackendConfigurator($provide) {
        $provide.decorator('$httpBackend', HttpBackendDecorator);

        function HttpBackendDecorator($delegate) {
            var proxy = function(method, url, data, callback, headers, timeout, withCredentials) {
                // create proxy for callback parameter
                var delayedCallback = function() {
                    // simulate network load with 2 sec. delay
                    var delay = 2000;

                    // Invoke callback with delaying
                    setTimeout((function() {
                        callback.apply(this, arguments[0]);
                    }.bind(this, arguments)), delay);
                };

                // delegate to the original $httpBackend call, but with the new delayed callback
                $delegate(method, url, data, delayedCallback, headers, timeout, withCredentials);
            };

            // the proxy object should get all properties from the original $httpBackend object 
            angular.extend(proxy, $delegate);

            // return proxy object
            return proxy;
        }
    }
})();

Monday, September 21, 2015

Promises in AngularJS. Part I. Basics.

There are many blog posts about Promises in general and Promises in AngularJS in particular. Promises are a part of the ES6 (EcmaScript 6) specification, so it is worth to learn them. In this blog post, I will try to summarize the most important info about Promises in a simple and clear way. The necessary code snippets will be provided too. The first part is about basics.

What is a Promise? Promise is a solution to avoid so-called "Pyramid of Doom". If you work with asynchronous operations (HTTP calls, file system operations, etc.), you know the situation where you have callbacks nested inside of another callbacks which are nested again inside of callbacks and so on. An example:
asyncOperationOne('first data', function(firstResult) {
    asyncOperationTwo('second data', function(secondResult) {
        asyncOperationThree('third data', function(thirdResult) {
            asyncOperationFour('fourth data', function(fourthResult) {
                doSomething();
            });
        });
    });
});
Every asynchronous operation invokes a callback function when the operation is completely done. In case of AngularJS and the $http service the "Pyramid of Doom" could look as follows:
var result = [];

$http.get('/api/data/first.json').success(function(data) {
    result.push(data);
    $http.get('/api/data/second.json').success(function(data) {
        result.push(data);
        $http.get('/api/data/third.json').success(function(data) {
            result.push(data);
            $scope.result = result;
        });
    });
});
We didn't show yet the error handling. Now imagine how it looks like with error handling. Terrible and not readable. Promises help to synchronize multiple asynchronous functions and avoid Javascript callback hell. The official concept of Promises is described in the Promises/A+ specification. With promises, an asynchronous call returns a special object called Promise. The calling code can then wait until that promise is fulfilled before executing the next step. To do so, the promise has a method named then, which accepts two functions - success and error function. The success function will be invoked when the promise has been fulfilled. The error function will be invoked when the promise has been rejected. An example of $http service based on promises:
$http.get('/api/data.json').then(
    function(response) {
        ...
    },
    function(error) {
        ...
    }
);
We can say, a promise represents the future result of an asynchronous operation. The real power from promises is the chaining. You can chain promises. The then function returns a new promise, known as derived promise. The return value of some promise's callback is passed as parameter to the callback of the next promise. In this way, you can access previous promise results in a .then() chain. An error is passed too, so that you can define just one error callback at the end of chain and handle all errors there. Here is an example of promise chaining:
$http.get('/api/data.json').then(
    function(response) {
        return doSomething1(response);
    })
    .then(function(response) {
        return doSomething2(response);
    })
    .then(function(response) {
        return doSomething3(response);
    }, function(error) {
        console.log('An error occurred!', error);
    }
);
If you return some value from the derived promise as shown above, the derived promise will be resolved immediately. A derived promise can be deferred as well. That means, the resolution / rejection of the derived promise can be deferred until the previous promise has been resolved / rejected. This is done by returning a promise from the success or error callback. The AngularJS' $http service returns a promise, so that we can rewrite the shown above "Pyramid of Doom" with $http as
$http.get('/api/data/first.json').then(
    function(response) {
        // response here comes from the GET to /api/data/first.json
        result.push(response.data);
        return $http.get('/api/data/second.json');
    })
    .then(function(response) {
        // response here comes from the GET to /api/data/second.json
        result.push(response.data);
        return $http.get('/api/data/third.json');
    })
    .then(function(response) {
        // response here comes from the GET to /api/data/third.json
        result.push(response.data);
        $scope.result = result;
    }, function(error) {
        console.log('An error occurred!', error);
    }
);
What happens now when a HTTP call in the promise chain fails? In this case, success callbacks in every subsequent promise in the chain will be skipped (not invoked). The next error callback in the promise chain however will be invoked. Therefore, the error callback is indispensable. An example:
$http.get('/api/data/wrong_or_failed_url.json').then(
    function(response) {
        // this function is not invoked
        return $http.get('/api/data/second.json');
    })
    .then(function(response) {
        // this function is not invoked
        return $http.get('/api/data/third.json');
    })
    .then(function(response) {
        // this function is not invoked
        $scope.result = response.data;
    }, function(error) {
        // error callback gets invoked
        console.log('An error occurred!', error);
    }
);
Instead of error callback you can also use an equivalent - promise's function catch. The construct promise.catch(errorCallback) is a shorthand for promise.then(null, errorCallback).

Be aware that either the success or the error callback will be invoked, but never both. What to do if you need to ensure a specific function always executes regardless of the result of the promise? You can do this by registering that function on the promise using the finally() method. In this method, you can reset some state or clear some resources. An example:
$http.get('/api/data.json').then(
    function(response) {
        // Do something in success case
    },
    function(error) {
        // Do something in failure case
    }).finally(function() {
        // Do something after either success or failure
    }
);
Where and how to use the promise examples above? A typical pattern in AngularJS is to have calls via $http in a service. Controllers call services and are not aware that $http is used. Example for MyController -> MyService -> $http:
// In MyService
this.fetchData = function() {
    return $http.get('/api/data.json');
};

// In MyController
$scope.fetchData = function() {
    MyService.fetchData().then(function(response) {
        return response.data;
    }, function(error) {
        ...
    }).finally(function() {
        ...
    })
};
We have to mention yet that AngularJS' implementation of promises is a subset of the library Q. Q is the most known implementation of the Promises/A+ specification. That's all for this short post. In the next post, we will meet the AngularJS' deferred object. A deferred object is an object that exposes a promise as well as the associated methods for resolving / rejecting that promise. It is constructed using the $q service - the AngularJS' implementation of promises / deferred objects inspired by Q.

Stay tuned!

Sunday, June 14, 2015

Create source maps at project build time for JavaScript debugging

If you're doing front end web development, your web resources such as JavaScript and CSS files might be minificated, transpiled or compiled from a completely different language. If you now want to debug the generated code in the browser, you can not do that because the output code is obfuscated from the code you wrote. The solution is to use source maps. A good introduction to the source map can be found in the articles "Introduction to JavaScript Source Maps", "Enhance Your JavaScript Debugging with Cross-Browser Source Maps" and "An Introduction to Source Maps".

I will try to summarize the quintessence about the source maps and concentrate on JavaScript only. Generating CSS by LESS / SASS is interesting too, but it is not yet supported by my Maven plugin I will present in this blog post. How to use a source map? In order to use a source map, your browser development tools need to be able to find it. If your generated code is JavaScript, one way to let the development tools know where to look is to add a comment to the end of the generated code which defines the sourceMappingURL - the location of the source map. For example: //# sourceMappingURL=mylib.js.map or //# sourceMappingURL=/mypath/mylib.js.map or //# sourceMappingURL=http://sourcemaps/mylib.js.map. If you now open a development tool and the source map support is enabled, the browser will stream down the source map which points to the original file and show the original file instead of generated one (minificated, compiled, transpiled, etc.). Now, you can set a breakpoint in the original file and debug it as it would be delivered with you web application! Such debugging is possible due to the fact that a source map provides a way of mapping code within a generated file back to it's original position in a source file. I found a nice bild here to visualize the entire process.

Image

Source maps are supported in all major browser that are shipped with built-in support for source maps. If you follow my links to the articles I mentioned above, you can see some examples for Internet Expoler 11, Firefox and Google Chrome. Chrome's Dev Tools enables the source maps support by default. Simple check if the checkbox "Enable JavaScript source maps" is enabled.

Image

If you a fan of Firefox, you should use the Firefox' Web Console (Shift + Ctrl + K) and check if the same option is enabled in settings too. Please note that Firebug doesn't support debugging by source maps. You have to use the native Firefox' Web Console as I said. Internet Explorer 11 also rocks with source maps. It has even more features - you can add source map files from your local disk. Simple right-click on a generated (e.g. compressed) file and select "Choose source map". The article "Enhance Your JavaScript Debugging with Cross-Browser Source Maps" which I mentioned above, has a lot of picture for IE and debugging TypeScript files.

Source maps can be created during your build. If you work with Node.js and npm, the best tool is UglifyJS - a popular command line utility that allows you to combine and compress JavaScript files. If you work with Maven, you can use my Maven plugin for resource optimization I wrote a some years ago and just updated for supporting source maps. It uses the superb Google Closure Compiler which offers a support for source map creation and many other cool features. Please refer the documentation of the Maven plugin to see how to set the various source map configuration options. The simplest configuration which we also use in the current PrimeFaces Extensions release looks like as follows:
<plugin>
    <groupId>org.primefaces.extensions</groupId>
    <artifactId>resources-optimizer-maven-plugin</artifactId>
    <version>2.0.0</version>
    <configuration>
        <sourceMap>
            <create>true</create>
            <outputDir>${project.basedir}/src/sourcemap/${project.version}</outputDir>
            <sourceMapRoot>
              https://raw.githubusercontent.com/primefaces-extensions/core/master/src/sourcemap/${project.version}/
            </sourceMapRoot>
        </sourceMap>
    </configuration>
    <executions>
        <execution>
            <id>optimize</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>optimize</goal>
            </goals>
        </execution>
    </executions>
</plugin>
By this way, only compressed JavaScript files will be packaged within released JAR file. Uncompressed files are not within the JAR. The JAR is smaller and free from redundant stuff. For the PrimeFaces Extensions, the source map and uncompressed files are checked in below the project root. For example, the folder with source maps and original files for the current release 3.2.0 is located here: https://github.com/primefaces-extensions/core/tree/master/src/sourcemap/3.2.0 That means, a compressed file, say timeline.js, has the following line at the end:

//# sourceMappingURL=https://raw.githubusercontent.com/primefaces-extensions/core/master/src/sourcemap/3.2.0/timeline.js.map

The source map timeline.js.map has the content (I truncated some long lines):
{
  "version":3,
  "file":"timeline.js",
  "lineCount":238,
  "mappings":"A;;;;;;;;;;;;;;;;;;;;AA4DqB,WAArB,GAAI,MAAOA,MAAX,GACIA,KADJ,CACY,EADZ,CAUsB,YAAtB,GAAI,...
  "sources":["timeline.source.js"],
  "names":["links","google","undefined","Array","prototype","indexOf","Array.prototype.indexOf",...]
}
It would be probably better to bring these files to a CDN, like cdnjs, but CDNs are not really intended for source maps. Another option would be a PrimeFaces repository. We will see how to handle that in the future. The next picture demonstrates how the debugging for the compressed JS file timeline.js looks in my Firefox.

Image

Due to the Firefox built-in source map support we see the uncompressed JS file timeline.source.js. I set a breakpoint on the line with the if-statement if (index < 0). After that I clicked on an event in the Timeline component. The breakpoint was jumped up. Now, I can debug step by step and see my local and global variables on the right side. As you probably see, the variable index is shown as a (I marked it red as var index). This is a shortcoming of the current source map specification. You can read this discussion for more details.

Again, keep in mind - the source maps and original files will be only loaded when you open up the browser dev. tools and enable this support explicitly. If the dev. tools has identified that a source map is available, it will be fetched along with referenced source file(s). If a source map file is not available, you will see a 404 "not found message" in the dev. tool (not bad at all in my opinion).

That's all. My next posts will be about AngularJS and less about JSF. Stay tuned.

Sunday, February 1, 2015

JavaScript Closures: Pass parameters to callbacks

Many JavaScript libraries allow to define callbacks. We often need to pass various parameters oder some context from outside to the same callbacks. JavaScript Closures makes it possible. The idea is to define two functions - one outer and one inner which acts as callback. The outer function can take parameters and pass them to the inner function. Thanks to Closures, the inner function can access the parameters in the parent scope. The outer function should now returns the inner one. That is actually a well-known trick.

Assume you use the Select2 plugin and want to have two callbacks. One is defined via the option templateResult and used for formatting select items in dropdown. Another is defined via the option templateSelection and used for formatting the displayed value in select field.
$(this).select2({
    ...
    templateResult: formatResultTemplate,
    templateSelection: formatSelectionTemplate
});

function formatResultTemplate(data) {
    ...
}

function formatSelectionTemplate(data) {
    ...
}
Assume, in both cases, the HTML code and formatting are similar except small differences in CSS styles. The functions have much repeated code and we would like to follow the don't-repeat-yourself principle (DRY). What is about to pass CSS style as parameter? And here you go (real example from project).
$(this).select2({
    ...
    templateResult: formatTemplate('margin:0 6px 0 0;'),
    templateSelection: formatTemplate('height:28px;')
});

function formatTemplate(style) {
    function formatWithStyle(data) {
        var imgSrc = $(data.element).data('image');
        if (imgSrc) {
            return "<img src='" + imgSrc + "' style='" + style + "'/>;<span class='option-text'>" + data.text + "</span>;";
        } else {
            return "<span class='option-text'>" + data.text + "</span>";
        }
    }

    return formatWithStyle;
}
The code is optimized now. The outer function formatTemplate returns the inner one formatWithStyle, "parametrized" with style. This inner function can be reused as "formatting callback".

I hope you got the idea.

Friday, November 7, 2014

Dynamic aria-live for better accessibility experience

Every web developer working with the web accessibility (WAI ARIA) knows or heard about aria-live. This is an attribute, which indicates a live region - any updatable HTML element - and describes the types of updates the user agents, assistive technologies (screen readers), and user can expect from the live region. The default value of the aria-live is off. That means, updates to the region will not be presented to the user unless the assistive technology is currently focused on that region. The value polite means, assistive technologies should announce updates at the next graceful opportunity, such as at the end of speaking the current sentence or when the user pauses typing. There is also the value assertive, which forces assistive technologies to notify the user immediately. In additional, the aria-relevant attribute can specify if the updates on the live region should only be spoken when the corresponding DOM node is added or removed or both.

Most time you need to set the value once and don't care about it. E.g. markup for error messages can contain aria-live="polite".
<div class="messages" aria-live="polite">
    ... some messages ...
</div>
Every time when the content of the div container with class="messages" is updated, it will be spoken by screen readers. That's good, but sometimes you only would like to notify a sightless user when the new (after update) and old (before update) contents are not equal. Imagine a railway ticket shop where users can choose fare class, number of traveler, book places, etc. The total buy price is permanently updated. We can write
<span aria-live="polite">
    <div id="pricetotal">
        <span class="hiddenOfScreen">Total price </span>
        #{price}
    </div>
</span>
and update the div by AJAX. #{price} is a current price as EL expression calculated on the server-side (coming from the JSF world, but JavaScript templating has similar expressions). Whenever the div gets updated, screen readers speak the total price regardless if the amount was changed or not. A more clever approach sets aria-live dynamically at runtime. If the amount was changed, the aria-live on the div should have the value polite. If the old and new prices are equal, the div should have the value off. The off value prevents speaking by screen readers.

The idea is to save the last updated price in the data-price attribute of the parent element and compare the old and new prices on every AJAX update. In the code snippet below, the div with id="pricetotal" gets updated. The JavaScript block inside is being executed on every update. We read and compare old and new prices and manipulate the aria-live attribute at runtime. Whenever the aria-live is set to polite, screen readers speak "Total price" and the amount.
<span data-price="#{price}">
    <div id="pricetotal">
        <span class="hiddenOfScreen">Total price </span>
        <span class="priceoutput">#{price}</span>
        <script type="text/javascript">
            $(document).ready(function () {
                $pricetotal = $('#pricetotal');
                var oldPrice = $pricetotal.parent().data('price');
                var newPrice = $pricetotal.find('.priceoutput').text();
                if (oldPrice != newPrice) {
                    $pricetotal.attr('aria-live', 'polite');
                    $pricetotal.parent().data('price', newPrice);
                } else {
                    $pricetotal.attr('aria-live', 'off');
                }
            });
        </script>
    </div>
</span>
If prices are not equal, the new price is saved in the data-price to make it available in the next update.

Sunday, October 12, 2014

WAI-ARIA support for AutoComplete widget

In this post I would like to discuss the accessibility for an AutoComplete widget. A typically AutoComplete widget provides suggestions while you type into the field. On my current work I implemented an JSF component on basis of Twitter's Typeahead - a flexible JavaScript library that provides a strong foundation for building robust typeaheads. The Typeahead widget has a solid specification in form of pseudocode that details how the UI reacts to events. The Typeahed can show a hint in the corresponsing input field, like the google's search field shows it, highlight matches, deal with custom datasets and precompiled template. Furthermore, the Bloodhound suggestion engine offers prefetching, intelligent caching, fast lookups, and backfilling with remote data.

Image

Despite many features, one big shortcoming of the Typeahead is an insufficient WAI-ARIA support (I would say it was completely missing until now). An AutoComplete widget should be designed to be accessible out of the box to users of screen readers and other assistive tools. I have decided to add a fully WAI-ARIA support, done this taks and sent my pull request to the GitHub. Below is the new "WAI-ARIA aware" markup with an explanaition (not relevant HTML attributes are omitted).
<input class="typeahead tt-hint" aria-hidden="true">

<input class="typeahead tt-input" role="combobox"
    aria-autocomplete="list/both"
    aria-owns="someUniqueID"
    aria-activedescendant="set dynamically to someUniqueID-1, etc."
    aria-expanded="false/true">
  
<span id="someUniqueID" class="tt-dropdown-menu" role="listbox">
    <div class="tt-dataset-somename" role="presentation">
        ...
        <span class="tt-suggestions" role="presentation">
            <div id="someUniqueID-1" class="tt-suggestion" role="option">
                ... single suggestion ...
            </div>
            ...
        </span>
        ...
    </div>
</span>

<span class="tt-status" role="status" aria-live="polite" style="border:0 none; clip:rect(0, 0, 0, 0); height:1px;
      width:1px; margin:-1px; overflow:hidden; padding:0; position:absolute;">
      ... HTML string or a precompiled template ...
</span>
The first input field with the class tt-hint simulates a visual hint (s. the picture above). The hint completes the input query to the matched suggestion visually. The query can be completed to the suggestion (hint) by pressing either right arrow or tab key. The hint is not relevant for the screen readers, hence we can apply the aria-hidden="true" to that field. The hint is ignored by screen readers then. Why is it not important? Because we will force reading the matched suggestion more intelligent by the "status" area with aria-live="polite" (will be explained below).

The next input field is the main element where the user inputs query. It should have a role="combobox". This is a recommended role for an AutoComplete. See the official WAI-ARIA docu for more details. In fact, the docu also shows a rough markup structure of an AutoComplete!

The main input field should have various ARIA states and properties. aria-autocomplete="list" indicates that the input provides autocomplete suggestions in the form of a list as the user types. aria-autocomplete="both" indicates that suggestions are also provided by a hint (additional to a list). The property aria-owns indicates that there is a parent / child relationship between the input field and the list with suggestions. This property should be always set when the DOM hierarchy cannot be used to represent the relationship. Otherwise, screen readers will get a problem to find a list with suggestions. In our case, it points to the ID of the list. The most interesting property is aria-activedescendant. A sightless user navigates through the list via arrow keys. The property aria-activedescendant propagates changes in focus to assistive technology - it is adjusted to reflect the ID attribute of the current child element which has been navigated to. In the picture above, the item "Lawrence of Arabia" is selected (highlighted). aria-activedescendant is set to the ID of this item and screen readers read to blind users "Lawrence of Arabia". Note: the focus stays on the input field, so that you can still edit the input value. I suggest to read more about this property in the Google's Introduction to Web Accessibility.

Image

The property aria-expanded indicates whether the list with suggestions is expanded (true) or collapsed (false). This property will be updated automatically when the list's state changes.

The list with suggestions itself should have a role "listbox". That means, the widget allows the user to select one or more items from a list of choices. role="option" should be applied to individual result item nodes within the list. There is an interesting article "Use "listbox" and "option" roles when constructing AutoComplete lists", which I suggest to read. Not important for the screen readers parts should be marked with role="presentation". This role says "My markup is only for non sightless users". You probably ask, what is about the role="application"? Is it important for us? Not really. I skipped it after reading "Not All ARIA Widgets Deserve role="application"".

The last element in the markup is a span with the role="status" and the property aria-live="polite". What it is good for? You can spice up your widget by letting the user know that autocomplete results are available via a text that gets automatically spoken. The text to be spoken should be added by the widget to an element that is moved outside the viewport. This is the mentioned span element with applied styles. The styles are exactly the same as the jQuery CSS class ui-helper-hidden-accessible, which hides content visually, but leaves it available to assistive technologies. The property aria-live="polite" on the span element means – updates within this element should be announced at the next graceful interval, such as when the user stops typing. Generally, The aria-live property indicates a section within the content that is live and the verbosity in which changes shall be announced. I defined the spoken text for the AutoComplete in my project as an JavaScript template compiled by Handlebars (any other templating engine such as Hogan can be used too).
Handlebars.compile(
    '{{#unless isEmpty}}{{count}} suggestions available.' +
    '{{#if withHint}}Top suggestion {{hint}} can be chosen by right arrow or tab key.' +
    '{{/if}}{{/unless}}')
When user stops to type and suggestions are shown, a screen reader reads the count of available suggestions and the top suggestion. Really nice.

Last but not least is the testing. If you do not already have a screen reader installed, install the Google Chrome extensions ChromeVox and Accessibility Developer Tools. These are good tools for the development. Please watch a short ChromeVox demo and a demo for Accessibility Developer Tools too. Alternative, you can also try a free standalone screen reader NVDA. Simple give the tools a try.