If you haven’t started playing with the Webflow features of Grails, here’s a short demo of Gravl’s Ajax-powered blog upload feature to whet your appetite…

Webflow is one of those technologies that is awesome for one particular aspect of your application: wizard-like things that take multiple steps! If you don’t use a technology like Webflow, you end up writing a simple state engine, and filling your session with all sorts of stuff to keep track of where your objects are up to.

As it turns out, Steven Devijver, one of the Grails founders, and a nice guy, has written a great book on Spring Webflow, so it’s not surprising that the Grails Webflow integration is first class.

I’ve just finished implementing the “Upload Pebble” feature where I can upload a zip file straight into my Gravl blog engine. It’s got the Ajax goodness going on, of course, to keep you posted on how your upload is going:

Ajax Upload in Progress

The sweet looking progress bar is courtesy of Bramus.

Putting Ajax calls in the middle of a Webflow turns out to be a bit of a non-trivial operation, but before we get complex, let’s look at how the flow working in practice. Here’s a pic of how things are implemented at the moment:

Flow of the Ajax Progress Bar

And the implementation in the controller looks something like this:

   def fileUploadFlow = {

        showDialog {
            on("upload") {
                // slurp in the zip here omitted
                importService.setZipFile(zipfile)
                return [ziptype: importService.supportedTypes ]
            }.to "showImportDetails"
            on("cancel").to "headHome"
        }

        showImportDetails {
            on("cancel").to "headHome"
            on("upload") {
                // use our flow-scoped service
                importService.setBlogId(params.blogId)
                importService.setBlogType(params.blogType)
            }.to "ajaxUpload"
        }

        // display ajax upload form
        ajaxUpload {
            on("update").to("refreshResults")
            on("cancel").to "headHome"
        }

        // refresh the ajax progress
        refreshResults {
            action {
                def percent = importService.percentComplete()
                [ percentComplete: percent ]
            }
            on("success").to "_webflowForm"
        }

        // send a webform with the results, unusual
        // _naming allows us to reuse the form
        // as a gsp template in the initial ajaxUpload view
        _webflowForm {
            on("update").to("refreshResults")
            on("complete").to("finishedImport")
            on("cancel").to "headHome"
        }

        finishedImport()

        headHome {
            redirect(action: index)
        }

    }

Those “update” and “cancel” state match the “Update” button in my gsps. So Webflow really simplifies the process of going through the wizard with all the state tracking done for me for free.

Now during the flow I need to keep track of all those input elements, then I need to invoke the upload process in a background thread so I can keep things whirring in the background while updating my progress counters from the UI.

What to do? Wouldn’t it be great if I could scope a fresh Grails Service object to the flow itself (kinda like a stateful service bean)? Turn out you can, with a one-liner to my ImportService:

 static scope = "conversation"

Then I can just reference importService in my flows and I always get a scoped copy. Voila!

The only genuinely tricky bit is the Ajax section. I’m using Prototype’s fabulous PeriodicalUpdater to do the regular polling, but Webflow does its state tracking through a _flowExecutionKey variable. Somehow I needed to serialise that value in each Ajax request to ensure that my session state was always A1.

I found the best solution for me was to have the Ajax call return a simple HTML form (with the appropriate hidden elements) and place that into a DIV. Then I could just make use of Prototype’s serialize() to get things into my parameters. Here’s my final magic (apologies for formatting):



new Ajax.PeriodicalUpdater('webflowFormDiv', '<g:createLink action="fileUpload"/>' ,
    {
    method: 'get',
    parameters: $('webflowForm').serialize(true) ,
    frequency: 10,
    decay: 2,
    onSuccess: function() {
        // hackery to delay update till the webflowFormDiv is refreshed... I wish onComplete() worked...
        setTimeout("myJsProgressBarHandler.setPercentage('myProgressBar', $('currentProgress').getValue())",500)
        if ($('currentProgress').getValue() == '100') {
            window.location = '?_flowExecutionKey=' + $('flowExecutionKey').getValue() + '&_eventId_complete=true'
        }
    }
});

        

One little bit of nastiness checks if we’ve reached 100%, then redirects the window to the “complete” event using the magic _eventId_complete call. During those periodic updates, my Ajax remote function just returns a small form to keep track of progress and the current valueof the _flowExecutionKey which changes on each call… (more dodgy formatting)

<form action="<g:createLink action="fileUpload"/>" id="webflowForm">
    <input id="currentProgress" value="${percentComplete}">
    <g:submitButton name="update" value="Update"/>
    <input id="flowExecutionKey" name="_flowExecutionKey" value="${request.flowExecutionKey}" size="100"/>
</form>

Well that’s probably more than you ever wanted to know about implementing a periodic Ajax updater for blog uploads. Now I’ve got a ton of sample data, I’m better placed to move onto some fun basic blog engine stuff. I’m thinking tag clouds might be a fun next stop…

Stay tuned…