Grails - Ajax

Ajax Support

Introduction

Ajax stands for Asynchronous Javascript and XML and is the driving force behind the shift to richer web applications. These types of applications in general are better suited to agile, dynamic frameworks written in languages like Ruby and Groovy. Grails provides support for building Ajax applications through its Ajax tag library. For a full list of these see the Tag Library Reference.

Rails developers will be familiar with the tag library as there are equivalent helper methods in Rails for most of Grails' Ajax tags.

Adaptive Ajax Tags

One major difference with Grails' tags is that they are not tied to the Prototype library. For the Grails Ajax tags to work you need to include a supported Ajax library within the "head" tag of your HTML document. The tags will then "adapt" to the included library.

Ajax with Prototype:

<g:javascript library="prototype" />

Ajax with Yahoo UI:

<g:javascript library="yui" />

Ajax with Dojo:

<g:javascript library="dojo" />

The tags can then be used as described in the following documentation without knowing the details of the underlying implementation. This allows you to choose which library is best suited for your needs as each provides its own benefits.

Installing Dojo

If you choose to use dojo because it is quite a large framework it does not come bundled with Grails and requires installing. Luckily, Grails provides a target to do this for you. Type the following from the root of a Grails project and dojo will be downloaded and setup automatically:

grails install-plugin dojo

Installing YUI

If you choose to use YUI, because it is quite a large framework, it does not come bundled with Grails and requires installing. Luckily, Grails provides a target to do this for you. Type the following from the root of a Grails project and YUI will be downloaded and setup automatically:

grails install-plugin yui

Loading Remote Content

Remote content can be loaded in a number of ways, the most commons way is through the "remoteLink" tag. This tag allows the creation of HTML anchor tags that perform an asynchronous request and optionally set the response in an element. The simplest way to create a remote link is as follows:

<g:remoteLink action="delete" id="1">Delete Book</g:remoteLink>

The above link sends an asynchronous request to the "delete" action of the current controller with an id of "1". This is great, but usually you would want to provide some kind of feedback to the user as to what has happened:

def delete = {
      def b = Book.get( params.id )
      b.delete()
      render "Book ${b.id} was deleted"
}

<div id="message"></div>
<g:remoteLink action="delete" id="1" update="message">Delete Book</g:remoteLink>

The above example will call the action and set the contents of the "message" div to the response in this case "Book 1 was deleted". This is done by the "update" attribute on the tag, which can also take a map to indicate what should be updated on failure:

<div id="message"></div>
<div id="error"></div>
<g:remoteLink action="delete" id="1"
              update="[success:'message',failure:'error']">Delete Book</g:remoteLink>

Here the error div will be updated if the request failed.

Handling Events

Specific javascript can be called if certain events occur, all the events start with the "on" prefix and allow you to give feedback to the user where appropriate, or take other action:

<g:remoteLink action="show" id="1" update="success" onLoading="showProgress();">Show Book 1</g:remoteLink>

The above code will execute the "showProgress()" function which may show a progress bar or whatever is appropriate. Other events include:

  • onSuccess (optional) - The javascript function to call if successful
  • onFailure (optional) - The javascript function to call if the call failed
  • on_ERROR_CODE (optional) - The javascript function to call to handle specified error codes (eg on404="alert('not found!')")
  • onUninitialized (optional) - The javascript function to call the a ajax engine failed to initialise
  • onLoading (optional) - The javascript function to call when the remote function is loading the response
  • onLoaded (optional) - The javascript function to call when the remote function is completed loading the response
  • onComplete (optional) - The javascript function to call when the remote function is complete, including any updates

Submitting a Form remotely

An HTML form can also be submitted asynchronously in one of two ways. Firstly using the "formRemote" tag which expects similar attributes to those for the "remoteLink" tag:

<g:formRemote url="[controller:'book',action:'delete']" update="[success:'message',failure:'error']">
       <input type="hidden" name="id" value="1" />
       <input type="submit" value="Delete Book!" />
</g:formRemote >

Or alternatively you can use the "submitToRemote" button this allows some buttons to submit remotely and some not depending on the action:

<form action="delete">
       <input type="hidden" name="id" value="1" />
       <g:submitToRemote action="delete" update="[success:'message',failure:'error']" />
</form>

Multiple buttons with formRemote

There is a problem with Prototype (up to and including 1.5.1 at the time of writing) that means Grails can not determine which button has been pressed when a formRemote tag includes more than one submit button. One simple workaround is to use a standard form with submitToRemote tags for the buttons. Here's an example:

<g:form name="editUserRolesForm" url="[ id: project.id ]">
  <!-- The table of roles that each user has in this project. -->
  <table class="roleTable">
    <!-- Put the role names in the header. -->
    <tr>
      <th/>
      <g:each in="${roles}" var="role">
        <th><g:link controller="role" action="show" id="${role.id}">${role.name}</g:link></th>
      </g:each>
    </tr>

<!-- Now output each user on a separate row. --> <g:each in="${users}" var="username"> <tr> <td>${username}</td> <g:each in="${roles}" var="role"> <td><g:checkBox name="role_${username + '_' + role.id}" value="${userProjectRoles.contains('role_' + username + '_' + role.id)}" /></td> </g:each> </tr> </g:each> </table> <span> <g:submitToRemote update="userRoleTable" action="saveUserRoles" id="${project.id}" value="Update" /> <g:submitToRemote update="userRoleTable" action="cancelEditUserRoles" id="${project.id}" value="Cancel" /> </span> </g:form>

Ajax and the Render Method

Grails' render method is the perfect companion for creating Ajax responses in a number of ways. As an example lets take a look at an example that retrieves the current date and time:

…
    // controller action
    def time = {
       render "${new Date()}"
    }
    …
    <g:remoteLink action="time" update="time">Get Time</g:remoteLink>
    <div id="time">Time to be displayed here</div>

Here the render method is used to update the "time" div with the current time. However, the render method can also be used to render markup:

…
    // controller action
    def time = {
       render(contentType:'text/xml') {
           time(new Date())
       }
    }
    …
    // resulting response
    <time>(the current time)</time>

But it gets even better, you can also render JSON responses using the same builder syntax:

…
    // controller action
    def time = {
       render(builder:'json') {
           time(new Date())
       }
    }
    …
    // resulting JSON response
    { time: "..." }

And if you want to leverage the OpenRico framework you can create OpenRico responses:

…
    // controller action
    def time = {
       render(builder:'rico') {
          element(id:'time') {
              new Date()
          }
       }
    }
    …
    // resulting Rico response
    <ajax-response>
          <response type="element" id="time">
               … // current time here
          </response>
    </ajax-response>