rc3.org

Strong opinions, weakly held

Tag: jQuery

Building a new service from scratch, followup

About a month ago I asked what people would use to build a new Web service from scratch, without providing any real details on what the application would do. People seemed to all agree that basing the service on JSON was the best way to go, and that’s the route I took, so I thought I’d write it up.

In this case, I have a PHP front end talking to a Java back end that uses Spring and Hibernate. The application in question is the administrative console for a test harness for a SOAP Web service. We use a third party SOAP service and needed the ability to provide responses that are guaranteed to always be the same for the purpose of creating tests. So my test harness stands in for the third party service, requiring only a URL change, and returns the data managed via the client application to which my question referred.

The application itself is very simple — it’s a single table with a lookup mechanism. The client enables users to find and edit individual records from the table. I needed four basic operations:

  • Search for records
  • Retrieve a single record
  • Update a record
  • Create a record
  • Delete a record

The Server

This is a Java application that uses Spring and Spring MVC. I use Spring’s Web Services support for the SOAP stuff, but it wasn’t worth it to bother with that for the administrative client. So instead I mapped every URL ending in .json to a Spring DispatcherServlet. Like all sensible Java developers who’ve worked in Rails, I hate configuration in general and XML configuration files in particular. So I used Spring’s ControllerClassNameHandlerMapping, which extracts the path from the URL and maps it to a Spring controller.

So a path like /myapp/responses/search.json calls the search() method in the ResponsesController class. Simple.

I won’t go into the boring details of my implementation, other than to talk about how I’m passing the messages back and forth. The client uses standard form submission (name and value pairs) to submit the data, and expects to get JSON back. So in the case of search, I extract the field name and query value, go to the database and run a query to retrieve the records in question, and then package them up and encode them using JSON to be sent back to the client.

The JSON encoding is handled by Google’s GSON library, and it’s ridiculously simple. It accepts a plain Java object, and serializes it and all of its children using reflection. Any properties that are null are omitted. So for my application, I created a wrapper object that was well-suited to being encoded using GSON. It includes a hash for error messages, a property for a single response object, and a collection of response objects for search results.

Then I created a view for Spring called SimpleGsonView, which accepts a wrapper (or any object), encodes it using GSON, and then sends the results to the client. Here’s the whole thing:

@Override
protected void renderMergedOutputModel(Map aMap, 
    HttpServletRequest aRequest, 
    HttpServletResponse aResponse) throws Exception
{
    Object wrapper = aMap.get("wrapper");
    Gson gson = new Gson();
    aResponse.setContentType("application/json");
    ServletOutputStream out = aResponse.getOutputStream();
    out.print(gson.toJson(wrapper).replaceAll("\\\\n", ""));
    logger.debug(gson.toJson(wrapper));
    out.flush();
}

One thing you may notice is that I do a search and replace on the JSON. It removes all occurrences of \n from the encoded data. That’s a hack to get around a client side bug that I got tired of trying to fix. I’ll talk about that later.

So for this application, to add new functionality, I just add a new method to my controller, process the input parameters and put the correct results in the wrapper, and then pass that to the view I created. Very simple. I added an autocomplete feature to the search page, and this is all I had to do to add support for it on the back end:

public ModelAndView complete(HttpServletRequest aRequest, 
    HttpServletResponse aResponse)
{
    String searchField = aRequest.getParameter("field");
    String searchQuery = aRequest.getParameter("query");

    List<String> results = Collections.emptyList();

    if (searchField.equals(FIRST_NAME))
    {
        results = getFirstNameCompletions(searchQuery);
    }
    else if (searchField.equals(LAST_NAME))
    {
        results = getLastNameCompletions(searchQuery);
    }
    else if (searchField.equals(KEY))
    {
        results = getKeyCompletions(searchQuery);
    }
    else if (searchField.equals(ZIP))
    {
        results = getZipCompletions(searchQuery);
    }

    return new ModelAndView(new SimpleGsonView(), "wrapper", 
        results);
}

The methods to retrieve the data are in another class, but I removed the reference to it for simplicity.

So that’s the server piece. I could have done more to make it RESTful but since the service is just for me, I went with the simplest possible approach.

The Client

For legacy reasons the client is a PHP application, and I decided to use jQuery and a little PHP to implement the client features. Here’s how each operation is implemented:

Search: The search feature is pure jQuery. The home page for the client has a search form on it with a pulldown of fields that can be searched and a text box for the search terms. I used the autocomplete feature from jQuery UI to make the search form a bit friendlier. I catch the submission of the search from (or the selection of an item in the list provided by autocomplete) and submit the results to the server using jQuery’s getJSON() method. Then I create a table on the page with the search results in it.

Retrieve a single record: This is the only place I used PHP to connect to the server. I decided that I wanted each item to have its own URL, and although I could have written the code to pull the key for the item in the URL, fetch it, and build the page in JavaScript, I opted to use PHP instead. To do so, I grabbed a copy of the Services_JSON library and included that in my app. PHP supports JSON natively starting with version 5.2, but my server is running PHP 5.1. I chose Services_JSON because it’s a single file. So when you go to the edit or show page, I use PHP to fetch the JSON for the key in the URL from the server and put it on the detail page or in the edit form. This is where the bug comes in. My service returns XML documents (encoded as a value in a JSON hash), and sometimes Services_JSON won’t decode the data properly. When I started stripping \n from the XML, it worked, so I went with it.

Create a record: This form uses jQuery’s post() method to submit the contents of the form to the server. If the request returns a successful result, it redirects the user to the detail page for the new item. If it doesn’t, it displays the errors included in the result.

Update a record: This form works just like the create form.

Delete a record: This is the simplest operation of all. It submits the key to be deleted, and the server simply returns true or false.

Upshot

This was just about the easiest implementation of an application and accompanying Web service I’ve ever done, in spite of the fact that I’m a pretty terrible JavaScript developer. The server side was much easier to implement than I’d anticipated, it took less code in Java than it would have in PHP, and not much more code than it would have taken in Ruby. This is the application that really convinced me that there’s essentially no development overhead involved with using AJAX rather than traditional server-side request processing when building Web applications, at least if you bring libraries like jQuery and GSON to the party to make things easier.

jQuery is awesome

I’ve been convinced for some time that jQuery is the best JavaScript library around. What I am coming to realize is that it’s also one of the best-run open source projects around as well. jQuery 1.3 (released today) has a new selector engine, a key component of any modern JavaScript library. To encourage other projects to adopt their selector engine, the jQuery project has turned it over to the Dojo Foundation, which currently manages the Dojo Toolkit, a jQuery competitor:

More importantly, though, we’re taking a big leap with Sizzle: We’re releasing it as a completely standalone project to be collaborated upon by many library creators and developers. We saw an opportunity to give something back to not just the jQuery community but to the JavaScript development community as a whole; and at the same time be able to collaborate with developers of other libraries on a single, unified, selector engine. We feel that there’s too much competition and not enough collaboration occurring and so we put our coud out on the line as a good first step towards working together.

As a sign of good faith and willingness to collaborate, we’ve turned over Sizzle to the Dojo Foundation (an excellent non-profit well suited for this project, not to be confused with the Dojo Toolkit). We wanted a common meeting ground where all developers would be able to work together and under which there would be a clear long-term copyright holder.

jQuery evangelism

For the past few weeks, I’ve been completely obsessed with jQuery, one of the many fine JavaScript UI libraries that are competing for your attention these days. I can’t really tell you whether jQuery is better than any of the other libraries, but I can say that it’s infinitely better than writing your own JavaScript by hand, and that it’s very approachable. You can start doing really powerful things with jQuery with a very small time investment, especially if you already know a little JavaScript and you understand CSS selectors.

Rather than provide a full primer on how jQuery works, I thought I’d provide an example of the kinds of thing jQuery makes easier. Here’s some real JavaScript code:

$(document).ready(function(event) {
    $("#sameAsPhysical").change(function(event) {
        if (event.target.checked) {
            $("#physical input").each(function() {
                $("#billing input[name='" + $(this).attr("name").replace(/physical/, "billing") + "']").val($(this).val());
            });
        }
    });
});

Here’s how the code works. I have a form that has two fieldset elements. One contains the fields for the physical address for a business, the other contains the fields for the billing address. Because the fields are in the same form, they need different names, so all of the physical address fields begin with physical and all the fields associated with the billing address start with billing.

The first thing I do in this code is bind a handler to the onChange event for a checkbox with the ID sameAsPhysical. Like every other JavaScript library, jQuery provides a facility for binding events to elements at page load time, allowing you to follow the tenets of unobtrusive JavaScript. In this case, I bind an anonymous function to $(document).ready(), which fires when all of the page content has loaded. I do all of my other event binding inside that anonymous function.

My goal here is to bind a function to the onChange event associated with my checkbox. Here’s where the “query” part of jQuery comes into play. To get a reference to the checkbox, I just use $("#sameAsPhysical"). As you can probably tell, that query selects an element with the ID sameAsPhysical, just like it would in a style sheet.

jQuery makes event binding simple by adding methods to selectors for each of the events that you can associate with them. So to bind a function to the onChange event, you just call change() on the results of your query. If the query returned a list of elements, the same function would be bound to all of the elements in the query results, as you’ll see in a moment.

Inside the event handler function, I need to check to see whether the checkbox (which has the label “Same as physical address”) is checked, and if it is, I copy all of the values from the “physical” fields to the “billing” fields.

jQuery helpfully passes in the event being captured to my function, so to check the state of the checkbox, I use the expression event.target.checked. If the box is checked, I copy the values. Now for the cool part.

First I query for $("#physical input"), which returns a list of all of the input elements inside an element with the ID physical. I call the each method on the results, which calls the function passed to it as an argument on each of the elements returned. Inside the function I’m going to copy the value in the field passed to each‘s callback to the value of the corresponding field in the other fieldset, containing the billing address fields.

Let’s look at this line of code:

$("#billing input[name='" + $(this).attr("name").replace(/physical/, "billing") + "']").val($(this).val());

First I grab a reference to the corresponding billing field for the physical field being processed. (Remember we’re inside a loop that’s iterating over the fields in the physical section of the form.)

jQuery goes beyond CSS selectors and provides some XPath-like features. In this case, I write a jQuery that looks like this #billing input[name='billing_address']. That returns any input fields in the billing section that have a name attribute set to billing_address. jQuery selectors are ridiculously powerful.

To create the billing_address name, I use a JavaScript expression: $(this).attr("name").replace(/physical/, "billing"). The trick here is that $(this) is a reference to the current element being passed into the callback function associated with the each method. So I replace “physical” in the name of that element with “billing” to grab the billing field that corresponds with this.

jQuery adds a convenience method to elements that are form fields, called val(). If you call it without an argument, it returns the value in that form field. If you pass it an argument, it sets the value of the field to the argument. So .val($(this).val()) sets the value of the field I looked up (using the name I constructed) to the value of this, which again is the form field passed in by each.

Needless to say this one line of code accomplishes a whole lot, and it’s par for the course with jQuery. You can get an amazing amount done with only a couple of lines of code. Here’s another much simpler example:

$("p.note").prepend('<span class="red">Note</span>: ');

This is a way to fake the ability to use the :before CSS pseudo-selector and the content property. It finds all of the paragraphs with the class note on the page and prepends my text label before the content in the tag.

These are all things you can do with JavaScript alone or with any of the libraries out there, but what I like about jQuery is that it allows you to accomplish these kinds of things with very little code, using a query language that’s easy for humans to comprehend. It doesn’t matter how powerful a tool is if you don’t actually use it, and I find jQuery to be a tool that’s easy to find ways to use. I highly recommend checking it out.

© 2024 rc3.org

Theme by Anders NorenUp ↑