Dojo is a powerful Javascript framework, useful for building browser-based Rich User Interface Applications (RIA). I had some annoying times recently getting a simple Dojo widget to display correctly, so this article captures a few lessons learned in taming that elusive widget. 

In passing I’ll mention that this blog does have a few allusive elements, referencing fine British Acoustic music. The blog title itself refers to the Summer Before the War, a lovely Huw Williams song, and this posting refers to the chorus of  Mike Silver’s beautifiul How Many Rivers.

Project contents

The Javascript I’m dealing with is organised in my Web project as shown here. Dojo itself is in the folder dojoroot and my code is in djna/main.js. I’m also using one data file in the folder djna/datastores.

image

Additionally I have a single HTML file, in my WebContent, which references that javascript. There are three sub-sections, described next.

Note: This article builds from my flawed-original code to the finally working version. The first version you see of a code snippet might need a little enhancement …

Loading Dojo

The first sections sets up a Dojo theme and loads Dojo itself:

  <html>
  <head>
    <title>A Device UI</title>  
    <style type="text/css">
      @import "../dojoroot/dijit/themes/tundra/tundra.css";
    </style>   
    <script type="text/javascript" src="dojoroot/dojo/dojo.js">
    </script>

Initialising the application

Then this code loads the dojo module with my application code in it:

   <script type="text/javascript" > 
      dojo.registerModulePath("djna", "../../djna");
      dojo.require("djna.main");
      dojo.addOnLoad(function(){
          djna.main.startup();                    
      });

Splash screen

As there is a little initialsation to do on applicaiton startup I display a reassuring message, in real-life this might be a nice splash screen.

</head>
   <body class="tundra" id="mainbody">
      <div id="loading"><P>SPLASH</P></div>  
   </body>
  </html>

Javascript Application

My intent is to display a simple grid with some data from the javascript in my datastore. I’m using the Dojo module pattern, where an anonymous function defines the public methods of a “Class”. So my Javascript is also in three parts: the “Class” definition, a main method which sets up the overall display, and utility method that creates the grid.

Class definition

My module outline looks like this, begining with the declaration of the module, which matches the name djna/main.js location of the Javascript file and which was referenced by the 

     dojo.require("djna.main");

statement in the HTML script.

dojo.provide("djna.main");

dojo.require("dojo.parser");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.layout.BorderContainer");

(function(){

     // main goes here

     // grid creationg goes here

})();

Modules referenced by the two metho implementation are made available by the dojo.require statements.

Main entry point

The main function has the responsibility to create a BorderContainer and request the creation of the grid.

       djna.main.startup= function(args) {                
            
            var grid = djna.main.makeGrid();            
                 
            var appContainer = new dijit.layout.BorderContainer({
              style: "width: 230px; height: 320px;  border:solid 1px",
              design: "headline"
            });

            dojo._destroyElement(dojo.byId("loading"));           
            dojo.place(appContainer.domNode, dojo.body(), "first");
            appContainer.addChild(grid);          
            appContainer.startup();                              
      };

The main then positions the grid in the Border Container and replaces the Splash screen with Border Container.

Grid Creation

The grid creation method sets up a data store and layout and then creates the grid

      djna.main.makeGrid = function(){
         var theStore = new dojo.data.ItemFileReadStore(
                  {url:"djna/datastores/dataonly001.json"});
          var layout = {cels: [                         {
                field: ‘custId’, name: ‘Customer Id’                     
            },{
                field: ‘name’, name: ‘Name’              
            },
            {
                field: ‘address’, name: ‘Address’              
            },
            {
                field: ‘postcode’, name: ‘Postcode’              
            }
            ]};
         var grid = new dojox.grid.DataGrid({
                id:"grid",
                store : theStore,
                clientSort : "true",
                structure : layout,
                region: "center"
            });
      };

 

Problems(1) – Drawing a Blank

This all looked (to my eyes) pretty plausible. I loaded the page and got this:

image

An empty box. And no error messages. I was running in Firefox with Firebug enabled. The console showed nothing untoward. So let’s use FireBug’s debugging capabilities. I bring up the Script tab in Firebug, and click the left margin to set breakpoints at the loading of my module:

image

I can now step to the call to djna.main.startup and step inside. Unfortunately, what I see is less than helpful:

image 

The debugging difficuty is caused by Dojo’s implementaiton of modules, using evals. However we can enable debugging.

Enabling Module Debugging

We adjust the Dojo configuration like this:

    <script type="text/javascript" src="dojoroot/dojo/dojo.js"
         djConfig="parseOnLoad: true, debugAtAllCosts: true">
    </script>

Now when we step into the module we can see our code:

image

and as we step through the code to line

     appContainer.addChild(grid);

we see this error:

image

Which suggests that a variable has not been intialised correctly. [The rather hard to understand variable name _17c  is shown because we are not using adevelopment version of Dojo.] A quick examination of the Watch tab shows that the grid variable is undefined:

image

despite the call to

    var grid = djna.main.makeGrid();

and so, we inspect the makeGrid() method more carefully and discover that a line of code is missing:

     var grid = new dojox.grid.DataGrid({
            id:"grid",
            store : theStore,
            clientSort : "true",
            structure : layout,
            region: "center"
        });
    return grid;  // <==== need this
  };

Now when we re-run we don’t see the “undefined” error, but unfortunately, we still see an empty page.

Problem(2) – Lay Me Low

The cause of this problem is a tiny type in the layout. I don’t know how I could have solve this problem efficiently, but in the end I obtained a working example and gradually modified it to match my broken code until I spotted the mistake.

     var layout = {cels: [                         {
                field: ‘custId’, name: ‘Customer Id’                     
            },{

The one letter change to:

     var layout = {cells: [                         {
                field: ‘custId’, name: ‘Customer Id’                     
            },{

gave some progress but yielded another problem …

Problem(3) – Just Got No Style

I’ll admit that solving this one took me an unreasonable amount of time. In retrospect when you see this:

image

it probably should be obvious that this is a style-sheet problem. However things were made a little more difficult by the fact that I had two errors.

Style sheet path

First, a simple path error in the html file:

   <style type="text/css">
      @import "../dojoroot/dijit/themes/tundra/tundra.css";  
    </style>

should be

    <style type="text/css">
      @import "dojoroot/dijit/themes/tundra/tundra.css";  
    </style>

That changes things slightly, but still we have a very similar bad screen:

image 

So there’s a further change:

Extra style sheet

It’s not obvious to a Dojo novice but you need a specific extra style sheet:

<style type="text/css">
@import "dojoroot/dijit/themes/tundra/tundra.css";
@import "dojoroot/dojox/grid/resources/tundraGrid.css";  

</style>

With that in place we can now see the grid taking shape.

image

But strangely, loading never completes, we never see any data. The final problem was probably the most annoying of the lot …

Problem(4) – Too Much Information

I suppose that if I had had the Dojo development source available it might have been possible to figure out what was happening by step-wise debugging. I took the approach of downloading a working example and comparing it with mine. Eventually I saw the error in my data file:

{
    identifier: "name",
    label: "name",
    items: [{
      custId: "123",   
      name: "Arnold Arkwright",
      address: "1, A Avenue; Accrington",
      postcode: "AA1 1AA"
    },{
        custId: "234",   
        name: "Bingley Bradford",
        address: "2, B Bridge; Batley",
        postcode: "BB2 2BB"
    }]
};

Can you see it? Yes, it’s that trailing semi-colon! Remove that and we get:

image

Conclusion

Well, I got there in the end and the journey taught me quite a lot. Seems like the tools such as Firebug do help quite a lot, but there are still quite a few gotchas.

Having used Apache Wink to  implement my basic JAX-RS service, which simply retrieves some information from Library, details of a Book Edition given its ISBN, I next want to implement an operation to add an Edition to the library. This operation will use a POST method to the resource

    http://myhost/LibraryWink/library/editions

that is, the resource that specifies that entire collection. Once again, the JAX-RS programming model makes the implementation of the service operation very easy, but I stumbled across a minor annoyance in testing the operation from Dojo, so in this posting I’ll take a side trip into the Dojo JavaScript libraries.

JAX-RS POST implementation

I already have a resource implementation class and so I just need to add a method to process the POST request

@POST
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public BookEdition addNewEdition(BookEdition edition) {
    return EditionStore.getInstance().addEdition(edition);
}

There are a few points to note here.

  • The input parameter edition is not annotated. Contrast this with the GET example,  getEditionByIsbn(@PathParam("isbn") String isbn), which enabled access to an element from the URI. Here the edition parameter is mapped to the request body, that is the content which is POSTed. Such a parameter is termed an Entity parameter. A method may have at most one entity parameter.
  • I have decided to permit both JSON and XML payloads to be Posted. JSON is very convenient from Dojo, as we shall see, but other clients may prefer XML. The Wink JAX-RS implementation will deal with deserializing JSON or XML depending upon the MediaType of the request.
  • As a design decision, I have chosen to return the state representation of the added edition. In this case it’s overkill as the state was passed in and we make no modifications in adding it to the Library. However in general it’s quite possible that there is some enrichment of data as it is inserted into the store. Hence I use the general pattern of returning a representation of what has been stored.

Testing with Poster

We can quickly test this method using a Firefox extension: Poster.

PosterPost

Specifying an Edition in XML

    <Edition>
      <title>Invincible</title>
      <author>John Power</author>
      <isbn>B000NZ8Y9Q</isbn>
     </Edition>

and the content type application/xml. The response is as expected, containing a representation of the Edition we added. In the same way, we can provide a JSON payload

    {"Edition":
      {"title": "Invincible",
       "author": "John Power",
      "isbn":"B000NZ8Y9Q"
    }}

And content type application/json. The content type of the response is determined by the HTTP Header field Accept with a value of application/xml or application/json.

HeaderJson

Testing from Dojo

One very likely scenario for using REST services is to call  an operation from an Browser-based rich client application. The Dojo JavaScript libraries are one possible technology for constructing such applications.

This is some sample code which invokes the addNewEdition() operation:

      var myEdition = {"Edition":{“author”:”x”, “title”"isbn":"44"}};
      var xhrArgs = {
                url: http://myhost/LibraryWink/library/editions,
                postData: dojo.toJson(myEdition),
                handleAs: "json",
                headers: { "Content-Type": "application/json"},
                load: function(data) {
                    dojo.byId("mainMessageText").innerHTML
                           =  "Message posted.";
                },
                error: function(error) {
                    dojo.byId("mainMessageText").innerHTML
                            = "Error :" + error;
                }
            };
        dojo.byId("mainMessageText").innerHTML
                           = "Message being sent…"; 
        var deferred = dojo.rawXhrPost(xhrArgs);

I’ll quickly summarise a few key features here, but first note in particular the actual POST request:

    dojo.rawXhrPost(xhrArgs);

You note that I use rawXhrPost rather than

    dojo.xhrPost(xhrArgs);

I can find no documentation that explains why rawXhrPost() is needed, but after much experimenting and imprecating I discovered that rawXhrPost() does indeed work and that xhrPost() sends no payload.

The other salient points:

  • postData: dojo.toJson(myEdition) converts the payload object to JSON.
  • headers: { "Content-Type": "application/json"} sets the MimeType in the header, hence allowing JAX-RS to correctly interpret the JSO string – the handleAs property relates to the interpretation of any response from the service
  • The url: http://myhost/LibraryWink/library/editions is our resource URI

Next – Unit Testing

So, that’s two ways of exercising a REST service, however as a test mechanism both leave a few things to be desired. In the next posting I’ll move on to using JAS-RS client