Grey-Bearded Geek

Random Thoughts Of A Middle-Aged Software Engineer

A Small Dose of jQuery

March 23rd 2009

A recent web application project had several user interaction scenarios that I realized would be much more user-friendly if implemented using some RIA technology.

If this were a large application, with a heavily-RIA interaface and some ramp-up time to burn, I would have considered either Flex or GWT. I’ve tried out both Flex and GWT, and I think that both have their merits. However, being RIA frameworks, both of these have a large impact on the structure of your application, are difficult (but not impossible) to use in an incremental way, and require a bit of infrastructure set-up to get things going. In this case, I was looking for something lighter weight.

Having worked extensively with “raw” JavaScript before, I knew that attempting to create a cross-browser RIA by hand is, as the saying goes, “non-trivial”. So, it looked like a good JavaScript library was in order.

I had used the Dojo Toolit previously, but found it a bit heavy, and poorly documented (things have probably improved, but I haven’t looked it in about 2 years). I was also aware of (but had not tried) other libraries such Prototype, MooTools, Script.aculo.us, and jQuery

I decided to evaluate some of these, but didn’t have a lot of time to do so. Whatever I picked was going to have to pass the “15 minute test”, in which the library at hand would get that much of my time to prove that it is capable of meeting my minimum requirements, and well-documented enough to allow me to put together a sample in that timeframe.

My RIA requirements were fairly simple: - dependent select lists - a grid control that supports:

  • server-side paging (ajax)
  • row selection
  • ability to read data from the grid, to send back to the server
  • modal dialogs

I started with jQuery, simply because I had been reading great things about it lately. As you can infer from the title of this blog entry, jQuery had what I needed, and I didn’t look any further.

The Evaluation At it’s core, jQuery is all about finding references to DOM elements (that’s the “query” part), and manipulating them. It’s syntax is very straightforward, and the selector syntax will be familiar to anyone with CSS experience. This is a very powerful concept, and enables things that would be next to impossible any other way.

jQuery Core is pure genius, but by itself, is a bit limited. What really makes jQuery shine is it’s support for plugins, and it’s rich plugin repository. Each of my requirements was satisfied by a different plugin.

Dependent select lists couldn’t have been simpler. After including Sam Collett’s Select Box Manipulation Plugin, it pretty much comes down to one line of JavaScript, using the plugin’s ajaxAddOption method. I tested it by simply creating a file with JSON data in it in the server’s path (next to my JSP files), and pointed the code at that.

In my case, I had a “Division Id” select list and “Site Id” select list, with the values for “Site Id” dependent on the selection in the “Division Id” list. I simply added the following the lines to the “Divsion Id” list’s onChange handler:

$("select#companyPartSiteId").removeOption(/.*/);

if ($("select#companyPartDivisionId").selectedValues()[0] != "") {
    $("select#companyPartSiteId").ajaxAddOption('sitesForDivision.json', 
        {"divisionId" : $("select#companyPartDivisionId").selectedValues()[0]}, false);
}

This means “remove all ‘option’ elements from the site id list, and then, if a value is selected in the division id list, make an HTTP Ajax request to the relative URL ‘sitesForDivision.json”, sending a “divisionId” parameter with the value that is selected in the division id list. Then, populate the site id select list with options from the data that is returned."

This is powerful, and simple to use. Everything is handled in one shot - no need to even get your hands dirty with parsing XML or JSON on the client. On the server, it looks like a “normal” HTTP GET request. Here is a sample of the JSON response that I put in my .json file:

{
"CODE01" : "Item One" ,
"CODE02" : "Item Two" ,
"CODE03" : "Item Three",
"CODE04" : "Item Four",
"CODE05" : "Item Five"
}

In the final implementaton, I kept the URL the same, wired the URL to a Spring controller, and simply wrote a JSP to output the JSON, like so: :::jsp <c:out value=“{”/> <c:forEach items=“${sites}” var=“site” varStatus=“status”> “${site.key}” : “${site.value}” <c:if test=“${!status.last}” >,</c:if> </c:forEach> <c:out value=“}”/>

If I had a lot of this to do, or the if the JSON data were complex, I’d have looked at creating a JSON View in Spring, and using a JSON serializer, but the requirement here was simple, and the JSP solution was equally simple and lightweight.

All in all, this took maybe five minutes to get going. Good stuff!

Grid Control I looked through a few jQuery grid plugins, and jqGrid clearly stood out as one of the best. Importantly, it is well documented.

Server-side paging was dead-easy - similar to what I’ve done in the past for DisplayTag, but using JSON data over Ajax, and no page refresh. As with the select list, I started with JSON data in a file, and then moved to a Spring controller to handle the request and talk to the service layer to get the data, and a JSP to write out the JSON data.

A grid control is a fairly complex widget, and accordingly, there’s a bit more to setting one of these up, though technically, it’s still just one line of JavaScript. As expected, one needs to set up things like the column headers, the column data model, whether the grid uses XML or JSON data, etc., as well as any handler methods for events such as “gridComplete”, “loadComplete”, “loadError”, etc.

The documentation is really good, but here’s a sample of my grid initialization: :::javascript var gridUrl = null; var datatype = “local”;

// don't make the request unless all criteria are present
if(partNumberField.val() != "" &amp;&amp; siteLabel.text() != "")
{
    _gridUrl = "supplierParts.json";
    _datatype = "json";
}

supplierPartsGrid = $("#supplierPartsGrid").jqGrid({
    url : _gridUrl,
    datatype: _datatype,
    datastr: "{}",
    mtype: 'GET',
    colNames:
    [
        'Status',
        'SPD',
        'Can Request',
        col_partNumber,
        col_supplierName,
        col_supplierId,
        col_country,
        col_partQual,
        col_generateRequest,
        col_edit
    ],
    colModel :[
        {name:'status',                   width:100, sortable:false, searchable:false, hidden:true},
        {name:'hasSupplierPartData',      width:100, sortable:false, searchable:false, hidden:true},
        {name:'canChangeSupplierRequest', width:100, sortable:false, searchable:false, hidden:true},
        {name:'supplierPartNumber',       width:150, sortable:false, searchable:false},
        {name:'supplierName',             width:350, sortable:false, searchable:false},
        {name:'supplierId',               width:100, sortable:false, searchable:false},
        {name:'countryOfOrigin',          width:100, sortable:false, searchable:false},
        {name:'partQualificationStatus',  width:125, sortable:false, searchable:false, align:'center'},
        {name:'generateRequest',          width:100, sortable:false, searchable:false, align:'center', 
            formatter:booleanFormatter},
        {name:'editbutton',               width:100, sortable:false, searchable:false, align:'center'}
    ],
    viewrecords: false,
    imgpath: 'jqgrid/themes/basic/images',
    hidegrid: false, // don't show the hide/show button
    loadtext: loadingMessage, // defined in jsp - so that it can be pulled from messages.properties
    loadui : 'block',
    postData: {criterionValue: partNumberField.val() + "|" + siteLabel.text()},
    forceFit: true,
    scrollrows : true,
    height: '15em',
    editurl: 'clientArray',
    afterInsertRow: afterInsertRow,
    gridComplete: onGridComplete,
    loadComplete: onLoadComplete,
    loadError: onSupplierPartsGridError
});

The one thing that took me a while to figure out was how to initialize the grid (on page load), without it making a request to the server, and then be able to have it request data on, say, a button click. The information is in the PDF document linked above, but not all in one place, some of it is incorrect, and it was not “intuitively obvious” to me. Here, then, is the recipe:

  • initialize the grid with a “datatype” of “local” (In some places, the documentaiton says “clientSide”. This may work in some version, but at least for the current version, the correct value is “local”.

  • initialize the grid with a null URL (not an empty string).

  • initialize the grid with a “datastr” value of an empty JSON array, i.e. “{}”

Then, when you wish to load the grid:

  • change the grid’s datatype to “json” (or xml if you’re using xml)

  • change the grid’s URL to the server-side url that will provide the data

  • set the grid’s ‘PostData’ (the parameters that will be sent in the request), if appropriate

  • trigger the grid’s reload handler ( grid.trigger(“reloadGrid”); )

Using the examples on jqGrid’s website, and reading the documentation, I was confident that this widget would meet my needs within the first 10 minutes or so. Getting the grid to behave and look just the way I wanted took several more hours during actual development, but I feel that it was worth it, and I would definitely recommend jqGrid.

The only thing that didn’t work out as expected for me was in-line editing of the grid. jqGrid’s in-line editing implementation is centered around the idea of communicating with the server for edit events. It can do client-side edits, but the grid’s behavior in this mode just didn’t feel right for my application. I decided to go with a column of ‘edit’ buttons in the grid, and a ‘pop-up’ modal dialog for editing a row’s data.

Modal Dialog I needed modal dialogs for a few different things in my application, including, as mentioned above, to show a form for editing a row of data in the grid. I didn’t want to actually pop up a new browser window - I never liked the way that looks, and it’s difficult to get actual modal behavior, but just for the one browser window / tab.

The jQuery UI sub-project’s Dialog widget turned out to be a simple-to-use, and elegant solution. Basically, you create a div in your HTML, and jQuery turns it into a dialog, with page-modal behavior, if desired. Initialization of the dialog looks a lot like initialization of the jqGrid (one method call, with a single argument that is an array of configuration parameters).

Here’s an example from my application: :::javascript supplierSearchDialog = $(“#supplierSearchDialog”).dialog ({ autoOpen: false, buttons: { “Ok”: vendorSearchDialogOk, “Cancel” : vendorSearchDialogCancel }, height : 520, width: 670, modal : true, bgiframe: true });

This looks deceptively simple. This one line creates a dialog that is modal, is constrained to the page on which the div exists, and which is draggable and resizable. The contents of the dialog is the original contents of the div, and is decorated with a header and footer containing the configured buttons. The button click events are wired to the supplied callback methods.

An additional, really nice feature is the “bgiframe”. This is actually a separate jQuery plugin that properly deals with IE’s z-index problems with rendering “lightweight” components in front of “heavy” components such as select lists. You can apply bgiframe to your own widgets separately, but the Dialog plugin will apply it for you if you specify this parameter.

This took no more that a couple of the 15 minutes of my “15 minute test”.

I probably spent a little more than 15 minutes evaluating jQuery, but after about 15 minutes, I was pretty certain that jQuery was going to meet my needs. At the end of the project, I was glad that I chose jQuery. The design of the library seems “just right” - easy things are easy to do, difficult things require a little more effort, but are still fairly easy, and many otherwise impossible things are possible with a little work.

Conclusion: I highly recommend jQuery!

blog comments powered by Disqus
Categories: Chariot, JavaScript, jQuery, Spring