Grails Filter Tricks: Ajaxify Controller Actions

Post by Josh Reed

A common requirement in the last couple of projects I worked on was the ability to create and edit domain classes via modal dialogs instead of Grail’s standard scaffolded pages. Originally I started down the path of embedding hidden forms in page and displaying them as needed. This quickly proved cumbersome on pages with many editable objects. I then hit upon the idea of fetching the forms via AJAX and displaying them in the modal dialogs. This worked pretty well but meant that I had to update some of my controller actions, typically ‘create‘ and ‘edit‘, to be aware of AJAX requests so they would only render the appropriate form instead of the whole page. The updates were relatively minor, usually as simple as adding a check of request.xhr and rendering a template:

def create() {
    def book = new Book()
    if (request.xhr) {
        render(template: 'createAjax', model: [instance: book])
    } else {
        [instance: book]
    }
}

This same pattern was repeated in every action I wanted to expose. It worked but was not very DRY, so I set out to find a better way. Since it’s the same model but routed to a different view for AJAX requests, I figured I might leverage the magic of Grails Filters. Filters give us a chance to intercept requests before they go to the controller (before), after the controller but before it gets passed to the view (after), and after the view is rendered (afterView). An after filter is just the ticket:

ajaxify(controller: '*', action: '*') {
    after = { Map model ->
        // only intercept AJAX requests
        if (!request.xhr) { return true }

        // find our controller to see if the action is ajaxified
        def artefact = grailsApplication
            .getArtefactByLogicalPropertyName("Controller", controllerName)
        if (!artefact) { return true }

        // check if our action is ajaxified
        def isAjaxified = artefact.clazz.declaredFields.find {
            it.name == 'ajaxify' && isStatic(it.modifiers)
        } != null

        def ajaxified = isAjaxified ? artefact.clazz?.ajaxify : []
        if (actionName in ajaxified || '*' in ajaxified) {
            render(template: "/${controllerName}/${actionName}Ajax",
                model: model)
            return false
        }
        return true
    }
}

The filter intercepts AJAX requests after they are processed by the controller. It looks in the controller to see if it declares a ‘ajaxify‘ static field. If so, and if the current action is listed in the ajaxify list, the filter takes the returned model and renders a template using the naming convention of: _<action>Ajax.gsp.

With this filter, the only change we need to make in a controller to expose actions is to declare them in a static field. The actions themselves remain unchanged:

class BookController {
    static ajaxify = ['create', 'edit']

    def create() {
        [bookInstance: new Book()]
    }
    ...
}

We also need to make sure that an appropriate template exists for the action, e.g. grails-app/views/book/_createAjax.gsp.

Bonus

As a bonus, here’s a quick little Javascript trick we use with this modal technique. The user should be able to perform their action even if something goes wrong with the AJAX request or if Javascript is disabled. To support this, we use a standard link to the controller action and then add a special CSS class for the links we want to trigger a modal:

<g:link controller="project" action="create"
    class="ajax-modal">Add Project</g:link>

We then use a little Javascript to progressively enhance the ajax-modal links to pop open a modal dialog instead of following the link:

$('.ajax-modal').live('click', function() {
    var url = $(this).attr('href');
    $('.modal').load(url, function() {
        // using the Twitter Bootstrap modal here
        $(this).modal({ show: true });
    });
    return false;
});

If all works well, the user will see the create/edit/etc. form in a modal dialog. If something goes wrong with the Javascript or AJAX request, the browser should take the user to the linked page.

Conclusion

While nothing earth-shattering, these little tricks have helped to DRY up a couple of my codebases. Hopefully they highlight the power of filters in Grails and are of use to you.

This entry was posted in Software Development and tagged , . Bookmark the permalink.

Related Posts:

5 Responses to Grails Filter Tricks: Ajaxify Controller Actions

  1. Pingback: Blog bookmarks 10/07/2012 « My Diigo bookmarks

  2. Pingback: Questa settimana in Grails (2012-41) - luca-canducci.com - Il blog di Luca Canducci: notizie, tips e nuove tecnologie dal mondo dell’IT.

  3. Pingback: An Army of Solipsists » Blog Archive » This Week in Grails (2012-41)

  4. Charu Jain says:

    I have been doing it in old way…passing the same model in ajax & normal views…..Nice tip laid by you.
    Bookmarking this page right away.

    Thanks for sharing.

  5. grails says:

    It would be cool if there is also a tag to easily compliment the ajaxification of the controller

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>