20 July 2009 13 Comments

Updating multiple page elements with Grails and Ajax

Every Web Application Project I’ve been involved in the recent past had one thing in common: the client demanded more and more rich-client features at an ever increasing pace – without committing itself to real rich-client frameworks such as Flex or Silverlight. This leaves us with Javascript / AJAX for implementing highly dynamic User Interfaces for Web Applications. A very common problem when working with Ajax is the requirement to update multiple parts of a page in response to a single request. This post aims to familiarize the reader with my own approach to this problem.

At its core my solution consists of just two functions – one running server side and the other executing on the client. The first function is intended to run on the server and is written in Groovy. It was originally written in Python for use with the Django framework and I’ve ported it to Groovy when I switched to Grails as my primary RAD Web framework. This function takes a map as argument and returns a JSON encoded map where the key is a valid jQuery Selector designating the targets for the update and the value is a map containing information how the matched elements should be modified and how the content for the update shall be produced. Don’t worry if you find this confusing at first. There’s a sample further down.

?View Code GROOVY
def renderElementUpdateResponse(elements)
{
  def rendered_elements = [:]
 
  elements.eachWithIndex
  { k, v, i ->
    rendered_elements[k] = [:]
    rendered_elements[k]['mode'] = v['mode']
 
    // if content is a Map, feed it into g.render - using the key as template name and the value as model
    if(v['content'] instanceof java.util.LinkedHashMap)
      rendered_elements[k]['content'] = g.render(template: v['content'].keySet().toArray()[0], model: v['content'].values().toArray()[0])
    else
      rendered_elements[k]['content'] = v['content']
  }
 
  def tmp = ['updates': rendered_elements]
  render tmp as JSON
}

The second function is written in Javascript and responsible for applying the JSON wrapped HTML fragments to their designated HTML DOM Elements:

?View Code JAVASCRIPT
function applyElementUpdates(transport)
{
  var updates;
 
  if (transport.updates)
  {
    updates = transport.updates;
    var scripts = new Array()  // need to queue initializers until all updates are done
 
    for(target in updates)
    {
      if(updates[target]['mode'] == 'execute')
      {
        code = updates[target]['content']
        scripts.push(code)
        continue
      }
 
      var targetNode = $(target);
      var newContent = updates[target]['content'];
      var oldContent = targetNode.html();
 
      switch(updates[target]['mode'])
      {
        case 'replace':
          targetNode.html(newContent);
          break;
        case 'insert':
          targetNode.html(newContent + oldContent);
          break;
        case 'append':
          targetNode.html(oldContent + newContent);
          break;
      }
    }
 
    // now run initializers
    for(i in scripts)
    {
      code = scripts[i]
      eval(code)
    }
  }
}

So how do you use it? Below you find all necessary code artifacts for implementing the described technique in arbitrary Grails application. Please note that this sample uses jQuery for carrying out the actual AJAX request but it would be trivial to port this over to any other decent Javascript Framework like MooTools, Prototype or YUI.


Let’s begin with the GSP page in /views/test/index.gsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head>
    <g:javascript src="jquery-1.3.2.js" />
    <g:javascript src="applyElementUpdates.js" />
 
    <script type="text/javascript">
    $(document).ready(function()
    {
      $('#btnClickMe').click(function()
      {
        $.ajax(
        {
          url: '${createLink(controller: 'test', action: 'update')}',
          type: "POST",
          dataType: "json",
          cache: false,
          async: true,
          success: function(data, status)
          {
            applyElementUpdates(data)
          }
        })
      })
    });
    </script>
  </head>
  <body>
  <input type="button" id="btnClickMe" value="Click me"/>
  <br/><br/>
  <div id="div1"></div>
  <div id="div2"></div>
  </body>
</html>

The controller in /controllers/TestController.groovy:

?View Code GROOVY
import grails.converters.JSON
 
class TestController extends BaseController
{
  def index =
  {
  }
 
  def update =
  {
    def model = [a: 1, b: 2]
 
    // Note: renderElementUpdateResponse is supposed to be implemented in BaseController
    return renderElementUpdateResponse(
    [
      '#div1'   : [ 'mode': 'replace', 'content' : [ 'fragment1': model ] ],
      '#div2'   : [ 'mode': 'append', 'content'  : [ 'fragment2': model ] ],
      '__init__': [ 'mode': 'execute', 'content' : 'alert("The page has been updated")' ]
    ])
  }
}

The call to renderElementUpdateResponse needs some explanation. Let’s step through the arguments line by line.

?View Code GROOVY
 '#div1': [ 'mode': 'replace', 'content' : [ 'fragment1': model ] ]

“[ 'fragment1': model ] ]” is the only relevant part on the server side. It means “render the GSP template /views/test/_fragment1.gsp, pass it the variable model as the model and store the resulting string as the content for the update. On the client side it tells the applyElementUpdates() function to replace (mode: replace) the output for this line in all elements with id=”div1″.

?View Code GROOVY
 '#div2': [ 'mode': 'append', 'content' : [ 'fragment2': model ] ]

Works basically the same as the previous line only that id=”div2″ is targetted and rendering /views/test/_fragment2.gsp will produce the content which is going to be appended to the target element rather than replacing it.

?View Code GROOVY
 '__init__': [ 'mode': 'execute', 'content' : 'alert("The page has been updated")' ]

In addition to updating DOM elements we can also send code to the client which is run after all elements have been updated. Such fragments are called initializers and are identified on the client by their mode attribute which must be ‘execute’. This sample also demonstrates that fragment content can be specifed directly (alert(“The page has been updated”)) instead of being produced by rendering a template. Please note that the selector (‘__init__’) is unused in this case. The sole reason for chosing that id was to emphasize the purpose of this fragment.

The final missing bits are the aforementioned Grails templates which are going to producing the content for the updated elements.

The fragment for updating the first div in /views/test/_fragment1.gsp:

<span>Value of A is ${a}</span>

The fragment for updating the second div in /views/test/_fragment2.gsp:

<span>Value of B is ${b}</span><br/>

Finally, opening the example page on your browser and clicking the button results in the following actions:

  • The div1 element gets updated with the HTML output of rendering /views/test/_fragment1.gsp against the simple model. The contents of div1 are replaced
  • The div2 element gets updated with the HTML output of rendering /views/test/_fragment2.gsp against the simple model. The HTML is appended to the div rather then replacing the contents.
  • An alert box is displayed

That’s it folks. I hope you found this post useful.

Tags:

13 Responses to “Updating multiple page elements with Grails and Ajax”

  1. marcin muras 23 July 2009 at 3:35 pm #

    Hi. Your solution is great. It is exactly what I was looking for :)
    I think such solution should be added to Grails core.
    Thanks !

  2. Stefan 9 September 2009 at 9:44 pm #

    Great solution for something thing I think should be available in the core or grails. Thanks for the effort!

    I have the javaScript for Prototype available for your post if you like.
    Also you could use if from your gsp with the remoteLink tag:
    Click Me

    Personally I like using the tags more then writing javascript for it myself.. :-)

  3. Rouven 25 October 2009 at 8:49 pm #

    Hi,

    big thx for this solution. I am very interested on the Prototype solution too.
    Can someone please publish it?

    Thanks and best regards

  4. Ashley 9 September 2010 at 11:35 pm #

    Hi,

    I’m having some problems with this strategy. I’m using g.render on the following GSP template:

    Hello World

    And, g.render returns the following:

    ntHello Worldn

    The control characters cause problems when I attempt to assign this to an html element using jQuery as follows:

    $(“#elementid”).html(renderedvalue)

    It looks like I have to strip control characters from the rendered StreamCharBuffer. Is that normal?

    Thanks

  5. Ashley 9 September 2010 at 11:40 pm #

    Sorry,

    In my previous post you could not see that the GSP page is comprised of Hello World contained by a paragraph element contained by a div element as follows


    Hello World

  6. oliver 10 September 2010 at 4:41 am #

    @Ashley: What kind of problems are you experiencing?

  7. ibarra 16 December 2011 at 12:21 pm #

    Hi Oliver,

    You’re using BaseController, which you extends for the testController.

    Where is this class? Can you post the code for it? Thanks.

  8. oliver 20 December 2011 at 11:36 am #

    @ibarra: Base Controller implements the renderElementUpdateResponse method show in the article.

  9. Mario 5 January 2012 at 2:48 am #

    Great post, I used it broadly in mu current development. However I’m facing a new challenge, trying to execute an ajax command for uploading a file. I dont know how to send the request info to get the file and be saved by the grails controller. Any Idea of how to do this?

  10. Dave 8 February 2012 at 12:10 pm #

    Hi,

    Thanks a million helped me do exactly what I needed to do, I also think it should be added to the grails core.


Trackbacks/Pingbacks.

  1. Grails Ajax Update zu kompliziert… | wicket praxis - July 21, 2009

    [...] zu werden. Wobei kompliziert eigentlich nicht das richtige Wort ist, denn man muss diese fehlende Funktionalität selbst ergänzen.Es ist unzweifelhaft möglich, so das gewünschte Ziel zu erreichen. Aber ich finde es spannend, [...]

  2. Introduction to Grails Development - Grails Tutorial - September 20, 2010

    [...] Updating multiple page fragments with Grails and Ajax by Oliver Weichhold [...]

  3. B1 » Blog Archive » Updating multiple page elements with Grails and Ajax - March 12, 2011

    [...] Write an javascript element update function with a nice interface and send JSON to it. [...]

Leave a Reply