I recently have been doing a lot of work in Dojo and sadly much of that work is modification/augmentation of the existing code to meet our needs, both extending and monkey patching.. First, I have to say when it comes to JavaScript frameworks the Dojo Data api and now Dojo Object Stores are not the best abstract data layer available, but they are also not he worst. They have made a good effort and honestly it is hard to avoid the “code my back end to match the framework JSON” model. If you want my opinion Ext/Sencha is still much stronger in this department, but Dojo is their only serious competitor and it’s also totally free!

Considering the lack of documentation and flexibility of some of the core parts of the data api I thought I would share some applicable work here (especially when you notice the dirth of user generated help on this topic as well.) In this case, some of the recent changes I made are very specific but some are widely re-usable. There is a lot I learned while messing around with this stuff, so as usual if you have any questions or just want to discuss things, send me a DM @scott2449 or comment below.

So what am I sharing? Well two bits of code that overcome weakness or enhance the JsonRestStore specifically. This component is located in the dojox part of the library and provides the tools to take a Restful web api that supports JSON as a return/content-type and will pump that into various dijits (dojo widgets) “automagically” for you. The idea is great and sort of avoids the “code my back end to match the framework JSON” problem I mention above by selecting two well known standard to help lock in the interface, JSON and REST. Problems are 1) Rest is not well defined and many people break what rules and principals exist 2) JSON is not strict semantically. This means that there is still no contract locking in URLs, return code, JSON structure etc… Considering that, the JsonRestStore makes two assumptions: 1) Restful URIs can only be of the form /Resources/id, obviously many out there do, but this is a wild simplification 2) Your JSON must have an items collection, as in, {items:[{},{}]} (This is actually a restriction that most dijits enforce, not the store)

Phew, so finally on to my fixes 1) I solved the items issue but not using any dijits and instead creating my own, which is for another story 2) I needed URIs of the form /Resource/id/NestedResouce/id/etc…. 3) I needed custom headers, which no Dojo Data classes support!

After digging and thinking for a while here is what I came up with:

1) I extend JsonRestStore to allow a custom request object, something that is built into the Rest service but that the JsonRestStore fails to leverage:

    dojo.declare("custom.EnhancedJsonRestStore", dojox.data.JsonRestStore, {

    "-chains-": {
         constructor: "manual"
    },

    constructor : function(options) { 
        dojo.safeMixin(this,options);
        this.service = dojox.rpc.JsonRest.services[options.target] || dojox.rpc.Rest(options.target, true, null, options.getRequest);

        this.inherited(arguments);
    }
    });

    //Usage
    var store = new custom.EnhancedJsonRestStore({
            target:"/my/target",
            syncMode:true,
            getRequest:{ //build request, including custom url & headers
                url: targUrl,
                handleAs: "json",
                contentType: "application/json",
                sync: true,
                headers: {
                   "Accept" : "application/json",               
                   "X-REMOTE-USERNAME" : "rahners"
                }
        });

You’ll notice that all I am doing is overriding the creation of the Rest service to pass 2 additional arguments, namely the request information as a constructor param so that you can modify it however you like: dojox.rpc.Rest(options.target, true, null, options.getRequest), see usage.

2) A monkey patch replacing the resolveJson method of dojox.json.ref. dojox.json.ref is the workhorse of the JsonRestStore, is does a complex toJson, that walks the json and creates an index to track dirty records etc … It is also used to generate the restful URIs, so the patch below, which is about 5 line into the resolveJson method, generates more complex ids:

    //...
    var target = defaultObject || it;

    //=================================================================================================================================================
    // BEGIN - Customizations to produce real RESTful URIs instead from representation hierarchy instead of the shameless excuse for URIs dojo is producing 
    //=================================================================================================================================================

    var targUrl = prefix.substring(0, prefix.length - 1);;
    var stack = new Array();
    var crawler = target;
    if( crawler.__parent && crawler.__parent.id ){
      if(id && id.indexOf('#')>-1){
        stack.push(id.substr(id.lastIndexOf('#')));
      }else{
        stack.push("Attributes/"+it[idAttribute]);
      }
    }
    else{stack.push(it[idAttribute]);} //push the target objects identifier to the stack, if the parent is a first level resource add /Attributes

    while(crawler.__parent){
      //Push the parents id or absolute (jsonref) id to the stack
      stack.push(crawler.__parent.id || crawler.__parent.__id.substr(crawler.__parent.__id.lastIndexOf('#')+1)); 

      crawler = crawler.__parent;
    }

    while(stack.length > 0){
      var curr = stack.pop();
      if(curr){
        targUrl+=(curr.indexOf('#')>-1?'':'/')+ curr; //pop them all off to build RESTful URI
      }
    }
    id = targUrl;

    //=================================================================================================================================================
    // END - Customizations to produce real RESTful URIs instead from representation hierarchy instead of the shameless excuse for URIs dojo is producing        
    //=================================================================================================================================================

    if(id !== undefined){ // if there is an id available...

If you read carefully you will see that with JSON like this:

{a:{
    id:1,
    b:{
        id:2,
        c:{id:3}
    }
}}

will be indexed as follows:

/a/1             {id:1,b:{id:2,c:{id:3}}}
/a/1/b/2         {id:2,c:{id:3}}
/a/1/b/2/c/3     {id:3}

If you were to say modify c, it would now result in a PUT to /a/1/b/2/c/3. Dojo, left to it’s own devices would do a PUT to /target/id which turns out to be /a/3 which is totally WRONG! Instead we traverse the JSON structure and produce a matching URI, of course it still is “code my back end to match the framework JSON”, however, it is a very common and reusable standard.

Any questions? Let me know!