I CAN HAS CODE?!?

Archive for the ‘Code Quickie’ Category

Code Quickie : Ensuring The Correct Accept Header In ExtJS

Wednesday, June 17th, 2009

When you are using any type of "respond to" functionality that is dependant on the Accept header in order to determine how to respond, it is important that any request you make against your application sends the appropriate headers.  This is a pretty simple thing to do when you are making your own AJAX requests, but not always that simple when using library components, such as the JsonStore, provided by ExtJS.  Below, I extend the built-in HttpProxy and TreeLoader to send the appropriate headers for the request.  These extensions are done in place and require no additional code … though, a word of warning, these extensions may break with future versions of the ExtJS framework.  The code below was tested with the RC1 release of ExtJS 3.0.

Ext.override(Ext.data.HttpProxy, {
    request: function(action, rs, params, reader, callback, scope, options) {
        if (this.conn && reader) 
        {
            this.conn.headers = this.conn.headers || {};
            
            if (this.conn.headers.hasOwnProperty('Accept') == false)
            {
                if (reader instanceof Ext.data.JsonReader) 
                    this.conn.headers['Accept'] = 'application/json';
                if (reader instanceof Ext.data.XmlReader) 
                    this.conn.headers['Accept'] = 'application/xml';                            
            }
        } 
        
        Ext.data.HttpProxy.superclass.request.apply(this, arguments);
    }
}); 

Ext.override(Ext.tree.TreeLoader, {
    requestData: function(node, callback){
        if(this.fireEvent("beforeload", this, node, callback) !== false){
            this.transId = Ext.Ajax.request({
                method:this.requestMethod,
                url: this.dataUrl||this.url,
                success: this.handleResponse,
                headers: {'Accept': 'application/json'},
                failure: this.handleFailure,
                scope: this,
                argument: {callback: callback, node: node},
                params: this.getParams(node)
            });
        }else{
            // if the load is cancelled, make sure we notify
            // the node that we are done
            if(typeof callback == "function"){
                callback();
            }
        }
    }
});

There’s not too much to this code.  In our extending of the HttpProxy we override the request method which is only defined in the HttpProxy’s base class (and consequently inherited).  In it, we check to see if there is a conn object as well as a reader and if there is, we check to see if an Accept has already been defined (just in case) and then we set the appropriate Accept header based on the type of reader that is being used.

For the TreeLoader extension, it’s not quite as simple.  Since TreeLoader directly extends from Observable there’s no method we can easily override without overwriting, so for this one, we have to copy the functionality from the TreeLoader implementation itself.  In doing so, we only have to add a headers property to the options for the AJAX request and we’re all set.

Code Quickie : ExtJS JsonReader With Mapping Support For Use In AIR

Wednesday, May 20th, 2009

So, yesterday I decided to play around with Adobe AIR, which I’m not familiar with, in conjunction with Ext JS, which I am familiar with.  Overall, if you are familiar with Ext JS on the web, you’re familiar with it in Adobe AIR … except for a number of unique issues due to Adobe AIR’s default sandbox, mostly revolving around the sandbox and what you can and can’t do in it.  Well, I ran into an issue tonight where I needed to use a JsonReader and use Record mappings with dot notation (i.e. binding a field to a child object in the JSON data).  My guess is that, as is the case with eval, new Function(…), which is what the default implementation of getJsonAccessor uses, is blocked by the sandbox.  I fixed it by extending the default JsonReader and providing a new implementation of getJsonAccessor using some code I created a while back for form binding in JavaScript.

JsonReader = Ext.extend(Ext.data.JsonReader, {
    exprToPath: function(expr) {
        var parts = expr.split(".");
        var path = [];
        Ext.each(parts, function(item, index, list) {
            var match = item.match(/([a-zA-Z0-9_]+)\[([^\]]+)\]/);
            if (match)
            {
                path.push(match[1]);
                if (/^\d+$/.test(match[2]))
                    path.push(parseInt(match[2]));
                else
                    path.push(match[2]);
            }
            else
            {
                path.push(item);
            }
        });
        return path;
    },  

    getDataValue: function(o, path) {
        var current = o;
        var rPath = path.slice().reverse();
        while (current && rPath.length > 0)
        {
            var key = rPath.pop();
            if (typeof current[key] !== 'undefined') current = current[key]; else return null;
        }

        return current;
    },

    getJsonAccessor: function(expr) {
        var self = this;
        var path = this.exprToPath(expr);
        return function(obj) {
            return self.getDataValue(obj, path);
        };
    }
});

It’s probably a bit of overkill for what is needed, but it does work well.