Lazy loading page content with jQuery UI, Ajax and Play 1.2

10 August 2011

Peter Hilton

by Peter Hilton

This article

This article shows you how to use jQuery UI components to load page content using Ajax from a Play framework 1.2 web application. This can be useful as a way of improving web page performance by delaying loading of some page content that might not be needed at first, or ever.

This follows on from the earlier article jQuery UI Ajax autocomplete with Play by providing examples of more jQuery UI components. As before, the lesson is that Play framework does not need to have its own built-in user-interface components because it works so well with existing JavaScript-based user-interface component libraries.

This article’s examples and source code are available as part of the Play framework jQuery UI module.

Architecture

In the simple model web application model, the server generates HTML documents that are rendered in a web browser. Ajax extends this model by using JavaScript code on the web client that acts as an Ajax client for additional server resources. This article is about the scenario where this Ajax client code loads additional web page content from the server, after the initial page has rendered, and adds this content to the page.

The client code consists of jQuery UI components with JavaScript code that manages the Ajax requests and updates the web page content. The server is a Play framework application that renders content as data in JSON format.

Accordion for lazy-loading content

The jQuery UI accordion widget groups sections of web page content so that only one section is shown at a time. Clicking a section’s header collapses one section and expands another. Although all of these sections can be part of the initial HTML document, we can also use this widget to avoid loading a section’s content until it is going to be displayed.

Client

The HTML part of the client consists of a div element that contains the the header for each section, without any section content, and two additional elements. The class name allows the JavaScript to select this element. Second is an HTML5 data attribute which we use to capture the server-side controller URL in the HTML document and therefore make it available to the JavaScript. We do this to avoid having to put the URL in the static JavaScript code, and so we can use the Play template syntax for generating the URL from a controller action reference.

<div class="accordion" data-url="@{jqueryui.Accordion.region('')}">
<h3><a href="#">Africa</a></h3>
<h3><a href="#">America</a></h3>
<h3><a href="#">Asia</a></h3>
<h3><a href="#">Australia</a></h3>
<h3><a href="#">Europe</a></h3>
</div>

The client-side JavaScript has two sections. The first simply adds the div elements that will hold the section content:

$('div.accordion h3').each( function() {
    $(this).after('<div></div>');
});

The second section turns this into an accordion component, setting various options.

$('div.accordion').each( function() {
    var $accordion = $(this);
    var serverUrl = $accordion.data('url');
    $(this).accordion({
        autoHeight: false,
        active: false,
        changestart: function(event, ui) {
            if (ui.newContent.is(':empty')) {
                $(ui.newContent).load(serverUrl + escape(ui.newHeader.text()));
            }
        },
        change: function() {
            $accordion.accordion('resize');
        }
    });
});

The active option is set to false to initially collapse all sections, so that no content is loaded until a section is selected.

The changestart option specifies a function that uses Ajax to load the content from the given server URL. The condition avoids loading the content if it has already been loaded, and the div is no longer empty. This event is triggered when a section is selected, but before updating the view.

The change option specifies a function that resizes the content div element to display the new content.

Server

The server-side is a Play controller action that renders a content section. The first part, for this example, is some test data - a list of ‘location’ names extracted from the JDK’s time zone list:

static Set<String> regionLocations(final String region) {
    final SortedSet<String> result = new TreeSet<String>();
    final String[] timeZones = TimeZone.getAvailableIDs();
    for (int i = 0; i < timeZones.length; i++) {
        final String[] parts = timeZones[i].split("/");
        boolean regionMatches = parts[0].equals(region);
        if (parts.length == 2 &amp;&amp; (region == null || regionMatches)) {
            final String location = parts[1].replaceAll("_", " ");
            result.add(location);
        }
    }
    return result;

The interface with the client-side widget is the following controller action, which simply renders the list of location name Strings for the given header name.

public static void region(final String header) {
    final Set<String> locations = TestData.regionLocations(header);
    render(locations);

The view simply renders a div element containing a comma-separated list of location names:

<div>#{list locations, as:'location'}
${location}#{ifnot location_isLast}, #{/ifnot}
#{/list}</div>

The result is an accordion, with content loaded from an Ajax request:

image

Tabs for lazy-loading content

jQuery UI tabs are similar to the accordion, in that they divide page content into sections such and display one at a time, but with a different layout. We can use the same technique as above to lazy load the tab contents.

Client

As before, the view consists of initial HTML that defines the sections, and some JavaScript. The HTML is a list of links to the controller action that will return the tab’s content:

<div class="tabs">
<ul>
<li><a href="@{jqueryui.Accordion.region('Africa')}"><span>Africa</span></a></li>
<li><a href="@{jqueryui.Accordion.region('America')}"><span>America</span></a></li>
<li><a href="@{jqueryui.Accordion.region('Asia')}"><span>Asia</span></a></li>
<li><a href="@{jqueryui.Accordion.region('Australia')}"><span>Australia</span></a></li>
<li><a href="@{jqueryui.Accordion.region('Europe')}"><span>Europe</span></a></li>
</ul>
</div>

The client-side JavaScript is even shorter in this case, the tabs plug-in supports Ajax loading out of the box.

$('div.tabs').tabs({
    ajaxOptions: {
        error: function(xhr, status, index, anchor) {
            $(anchor.hash).html('Error loading tab');
        }
    }
});

Server

For the server we can simply re-use the existing jqueryui.Accordion.region(String header) controller action that we used for the accordion, since the content is the same.

The result is a set of tabs:

image

Conclusion

Once you get used to jQuery and jQuery UI widgets, it is extremely easy to use Play to implement an Ajax-based user-interface. This allows the different people involved to do what they do best: the jQuery UI developers make the widgets work across multiple browsers, leaving the web application developer to implement a server-side with Play and wire the two together with a little JavaScript.