Vishful thinking…

Writing better javascript – Part 8

Posted in javascript by viswaug on November 21, 2008

JavaScript event model

This is another practice I have adopted in my JavaScript development to enable the components I create be able to raise custom events and have an event model that will support consumers to be able to use them as needed. That said, please do let me know if this event model can be improved upon in anyway possible. Always open to comments and suggestions…

In the code illustration below, I am adding the ‘click’, ‘select’, ‘unselect’, ‘enable’ and ‘disable’ event support to my MapTool JavaScript component. If you have been building mapping application then it should be evident why I would need those events on my map tools. I create a private listeners object where I store the listeners for every supported event as properties with the same name as that of the event. That makes it a little easier to retrieve the array of listeners for a certain named event. I also throw an error if the user tries to add a listener to an event that has not been pre-defined. This is obviously optional and be handled a couple of different ways. If the user tries to add a listener to an event that has not been pre-defined, the add listener call can just be ignored or a new event with that name can be added to the listeners object (but I don’t have such requirements in my applications so I throw an error). I also use the code snippet from John Resig (jQuery’s father) to be able to remove items from an array. That function can also be added to the array prototype if you are comfortable with messing the Array prototype.

var MapTool = function(toolID, options) {
    //private properties
    var _listeners = new Object(); //Create an object that hold the arrays of listeners as its properties
    //Predefine known events on the object – optional
    _listeners.click = new Array();
    _listeners.select = new Array();
    _listeners.unselect = new Array();
    _listeners.enable = new Array();
    _listeners.disable = new Array();

    //Adding John Resig’s array remove function
    _removeFromArray = function(array, from, to) {
        var rest = array.slice((to || from) + 1 || array.length);
        array.length = from < 0 ? array.length + from : from;
        return array.push.apply(array, rest);
    };
    //

    var _raiseEvent = function(eventName, e) {
        if (_listeners[eventName]) {
            for (var i = 0; i < _listeners[eventName].length; i++) {
                if (_listeners[eventName][i]) {
                    _listeners[eventName][i](e);
                }
            }
        }
    };

    var _clearListeners = function(eventName) {
        if (_listeners[eventName]) {
            _listeners[eventName] = new Array();
        }
    };

    return {
        createListener: function(eventName) {
            if (!_listeners[eventName]) {
                _listeners[eventName] = new Array();
            }
        },

        addListener: function(eventName, listener) {
            if(typeof(eventName) !== ‘string’ || typeof(listener) !== ‘function’) {
                if (!_listeners[eventName]) {
                    throw “The event ‘” + eventName + “‘ is not supported by MapTool.”;
                    //_listeners[eventName] = new Array(); – this is another option if you don’t want to throw an error if the user tries to add a listener for an unknown event
                }
                return { name: eventName, index: _listeners[eventName].push(listener) };
            }
            else {
                throw “Cannot add event listener. Invalid argument. A valid event name and listener function is required.”;
            }
        },

        removeListener: function(eventObj) {
            if (eventObj && eventObj.name && (eventObj.index || eventObj.index === 0)) {
                if (_listeners[eventObj.name]) {
                    _removeFromArray(_listeners[eventObj.index], index);
                }
            }
            else {
                throw “Cannot remove listener. Inavalid event listener object.”;
            }
        }
    };
};

 

The above event model has been working pretty good for me and the requirements in my applications.

Writing better javascript – Part 7

Posted in javascript by viswaug on November 21, 2008

The options argument

This practice is something that came to me after I ran into some flexibility issues with the JavaScript components I was creating. If you have been following this series, you might have noticed that I use the module pattern to create my JavaScript classes. As our JavaScript library started growing in size and the components I was creating was getting more complex, there was a need to be able to allow for ways to configure them wherever possible. Coming from the .NET universe, I started doing what I would have done if I was writing C# classes. I started modifying the constructor for the classes and started adding the different configurations points as arguments to the constructor like below.

var FormHelper = function(formID, cssClass, validationRules, submitHandler, cancelHandler/*constructor arguments defined here*/) {

    //Declare private variables here

    //Declare private methods here

    return {

    //Declare public properties here

    //Declare public methods here

};
};

The class just started off with ‘formID’ as the only argument. But as I started adding more extensibility points, I had started adding more arguments seen above like ‘cssClass’, ‘validationRules’, ‘submitHandler’ and ‘cancelHandler’ etc. These arguments started getting out of hand as I needed more extensibility points. And since JavaScript is JavaScript, I had to pass in the correct arguments in the exact index at which the argument is expected. Not a real good way of doing things. Thinking about it a little more and dwelling on it for a while I realized that the solutions was just to take an options argument that contains all the extensibility points as properties on it. See the changes below

var FormHelper = function(formID, options/*constructor arguments defined here*/) {

    //Declare private variables here

    //Declare private methods here

    return {

    //Declare public properties here

    //Declare public methods here

    };
};

var formOptions = {
    cssClass: “someClass”,
    validationRules: rules,
    submitHandler: handleSubmit,
    cancelHandler: handleCancel
};

var test = new FormHelper(“LatLongForm”, formOptions);

Doing it that was much cleaner. And didn’t have to change the definition of the constructor every time I wanted to add more extensibility points. Once I started writing my classes that way, I started noticing that the above pattern was used all the major JavaScript libraries. Probably should have seen the reason behind it earlier when I was using those libraries. But, now I know and am probably a better developer for it. But you don’t have to make that mistake and learn from my experience and follow this pattern from the get go.