3 September 2009
Grails App in ~40 hrs (part 5)
Welcome back to our effort to build a Grails app in less than a week. This post will cover the final functional aspects of the application, after which we can move on to trying to deploy the app into a Cloud Computing platform.
To recap briefly our progress to date:
- Part 1: We introduce the basic challenge: Build a non-trivial app in about 40hours.
- Part 2: We talk about the basic app concept.. a todo list manager called (cleverly) twodo. We have decided on the general technical framework (Grails, etc)
- Part 3: The first ‘technical’ post. JSecurity setup, initial Domain object created.
- Part 4: The UI is refined, and we start using the GrailsUI plugin to populate a YUI DataTable via AJAX.
So, although this post it titled #5, its really only the third technical post. In the last post we made our UI a bit more pleasing (or at least more intentional), and introduced the GrailsUI plugin into the mix for purposes of using AJAX to dynamically update a YUI DataTable. In this post we will put the finishing touches on the core application, which primarily involves getting the GrailsUI DataTable to work the way we want to.
This post will be a bit on the long side, because I want to finish up the core application so the next couple of posts can deal with deploying the twodo application into a Computing Cloud like Google’s AppEngine or Amazon’s EC2.
DataTable and Cell Editing
In the last post we populated a single simple YUI data table with test data using the GrailsUI, and it did exactly what we asked of it. However, our current implementation produced a simple read-only table. I envisioned using a DataTable construct to not only display the data, but to allow the user to dynamically update it via AJAX– sorta like an online spread sheet type application (in a similar fashion to the YUI example provided here.)
To this end, I initially installed the latest (v1.0.4) version of the plugin as per standard instructions, and it worked fine for our initial tests– however, it did not allow for the editing of the table cells. As it turns out this is a known limitation of the current stable version of the plugin. So now what? Well, working The Google On The Internet Machine revealed that the 1.1-SNAPSHOT has inline cell editing capabilities so I downloaded the 1.1-SNAPSHOT version from the plugin site and installed it from my local drive (grails install-plugin /path/to/download/grails-grails-ui-1.1-SNAPSHOT.zip)
Recall that in the last post we used the gui:datatable tag to construct a simple DataTable like so:
1 2 3 4 5 6 7 8 9 | <gui:dataTable id="dt_1" columnDefs="[ [id:'ID', formatter:'text', hidden:true], [status:'Status', formatter:'text'], [description:'Description', formatter:'text'], [est:'Est', formatter:'text'], ]" ... /> |
The snapshot version of the tag supports additional editor configuration parameter to be passed along as part of the columnDefs, that allow for the specification of an AJAX target to be called when the cell is edited. Lets take a few baby steps and modify the ’subscription’ column to allow for editing:
1 2 3 4 5 6 7 8 9 10 | <gui:dataTable id="dt_1" columnDefs="[ [id:'ID', formatter:'text', hidden:true], [status:'Status', formatter:'text'], [description:'Description', formatter:'text', editor:[controller:'home', action:'tableChange']], [est:'Est', formatter:'text'], ]" ... /> |
Note the additional code on line 6 where we indicate that changes to a cell in the ‘description‘ column are to be handled by making an AJAX call to the ‘tableChange‘ action in the ‘home‘ controller. Let’s point our browser at the page (http://localhost:8080/blogtwodo/home/datatable), and see what happens when we try to edit a cell in the description column.
OK. As the screen shot above indicates, when we click the cell to edit it, some JavaScript automagically renders a little dialog box for us with some basic editing options. Perfect.
But when we continue and try to actually change the value of the cell and save it by clicking on the ‘Save’ button, we get the following ugly error message:
Hey! Whats with the error? Well, the DataTable is attempting to make an AJAX call to the tableChange action– which does not exist yet. So let’s remedy that right now:
package com.alterlabs.twodo class HomeController { ... /** * AJAX Call */ def tableChange = { // TODO: Verify that this task belong to the current user! def task = Task.get(params.id) if ( params.field == 'description') { task.description = params.newValue task.save() } render "table changed" } }
Our initial cut at the tableChange method is about as straight forward as it gets. We load the Task by id (without even checking to see if it belongs to our user! Yikes) and save the new value. Simply reloading our home page after saving verifies that our changes to the description field were indeed saved.
params.id, params.newValue and the params.field parameters are send along as part of the request. These parameters are ‘hardcoded’ by the GrailsUI Plugin. I don’t recall finding this anywhere in the documentation, but viewing the source of the rendered page reveals it obviously enough. It’s also mentioned in this JIRA entry.
Now that we have the basic approach down, we can use what we have learned as a basis for constructing the real code that will be used to manipulate the two task lists in the application.
We can now add the additional configuration to provide editing for the other columns in the DataTable, as well as corresponding logic in the controller to handle the updates, like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <gui:dataTable id="dt_1" columnDefs="[ [id:'ID', formatter:'text', hidden:true], [status:'Status', formatter:'text', editor:[controller:'home', action:'tableChange', type:'dropdown', config:[dropdownOptions:['open','complete','delete'],disableBtns:true]]], [description:'Description', formatter:'text', editor:[controller:'home', action:'tableChange']], [est:'Est', formatter:'text', editor:[controller:'home', action:'tableChange']], ]" ... /> |
Notice that in addition to the basic cell editing, we also specify a bunch of other nifty options to the to the g:datatable control that help customize the behavior of the editor. On line 6 we indicate that the status column is to use a dropdown list for editing (with the list values of ‘open‘, ‘closed‘ and ‘delete‘– we will use the ‘delete’ option later), and that the default save/cancel buttons are to be hidden. In the tableChange action we make the obvious/appropriate corresponding changes to handle the edits to the additional fields.
Up and until now we have been fooling around with the DataTable in a test page. We want the contents of our two task lists to be rendered as the users home page after they successfully log in, so its about time we reincorporate what we have learned back into the main application (views/home/index.gsp) Taking the sample code above and embellishing it a bit with CSS gets us something like this:
So we have basic editing, but its still lacking a few fundamentals– notably callback notification on success/failure of the edit. We will address that in a bit below…
Adding Rows
So far all of the data we have been manipulating had been manually added as fixture data in the last post. Obviously a task management application needs a way to add new tasks so lets tackle that now.
Normally adding a new Task in a conventional non-AJAX fashion is trivial. (Actually, come to think of it, the standard Grails taglibs make using AJAX pretty trivial as well.)
However, accomplishing the same thing using AJAX and our DataTable, however, proves to be a tad bit trickier. Lets start again with baby steps and (reasonably) add a g:remoteForm at the bottom of our Work tasks list that submits via AJAX to an action in the home controller (once we figure it out for the Work tasks, we can replicate it for the Personal)
<g:formRemote name="worktask_form" onSuccess="alert('Success!')" url="[action:'addRow',params: [ taskType:'work']]"> <input type="text" name="newTask" id="newTask"/> <button type="submit">Add</button> </g:formRemote>
We put a simple alert() in the onSuccess event to prove things are working. Exercising our code thru the browser gives us the alert message we expect, but our DataTable is not yet updated to show the new row. If we refresh the page, we see the new row, but that’s hardly sufficient.
The obvious solution is to register a callback function that in turn forcibly refreshes the DataTable. Surprisingly, (at least according to the hours I spent trying to get this aspect of the system to work,) the YUI DataTable possesses no such straightforward function. The GrailsUI DataTable Object does, however, include a requery() function, which gets us 99% of the way there, so as a final resort I coded my own refresh() function based on this and extended the YUI DataTable Object by adding it to the javascript prototype:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Used to refesh our contents after a row change. // Basically a copy of requery() with a new Callback GRAILSUI.DataTable.prototype.refresh = function() { ... // NOTICE: This is what has changed from the requery() function var oCallback = { success: this.onDataReturnReplaceRows, //<<- this line failure: this.onDataReturnSetRows, scope: this, argument: newState }; ... }; |
We can then make our onSuccess callback invoke this new function, and test it out:
<g:formRemote name="worktask_form" onSuccess="GRAILSUI.dt_1.refresh();" url="[action:'addRow',params: [ taskType:'work']]">
This time, adding a new row refreshed our DataTable and the new row shows up in our list– which is awesome.
Deleting Rows
Now that we can add Tasks to our list, we need to also provide a mechanism for removing them. Recall that we configured our dropdown list for the ‘status‘ column to allow the for three values: ‘open‘, ‘closed‘ and ‘delete‘. The plan is to use a cell edit on the status column to trigger the deletion of the Task. With that in mind I added the following code to the tableChange action (which is the action to be called during our cell edits):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.alterlabs.twodo class HomeController { ... def tableChange = { def profileInstance = Profile.current() def task = Task.get(params.id) ... if ( params.field == 'status') { switch (params.newValue) { case 'open': task.completionDate = null task.save() break case 'closed': task.completionDate = new Date() task.save() break case 'delete': // no easy way to tell what list it was from so remove from both profileInstance.removeFromWorkTasks(task) profileInstance.removeFromPersonalTasks(task) profileInstance.save() println "*** Deleted task ${task}" break } } ... } |
removeFromWorkTasks() AND removeFromPersonalTask() (lines 19 and 20)? Currently there is no simple way to tell which list our Task belonged to, so we swing a heavy hammer and delete from both Task lists to cover our bases.
When we change our status column to ‘delete‘ we see a rewarding message in the console, but our DataTable does not update to show any change. We have to refresh the page for the DataTable to show an updated list. Why? For that we move to the next section….
Custom GrailsUI DataTable Changes
One thing that became quickly obvious was that as great as the GrailsUI plugin is, it still lacking in a few areas. Notably there is no mechanism for notifying the UI of a successful cell edit. This is not a big deal when we are simply editing the contents of an existing row (because our browser screen is naturally kept in sync)… but it becomes a definite issue when removing a row from the DataTable.
What we need is the ability to register a callback with underlying YUI structures of a function to be called after a DataTable cell edit. If we view the browser page source and look at the underlying javascript that the g:dataTable tag generates for us we can see what is up:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ... var registerEditorListener = function(editor, field, url) { GRAILSUI.dt_1.loadingDialog.show(); var editorCallback = { failure: function(o) { // revert the cell value GRAILSUI.dt_1.updateCell(oArgs.editor.getRecord(), field, oArgs.oldData); // alert user alert('Recieved an error during edit: ' + o.responseText); } }; YAHOO.util.Connect.asyncRequest('POST', url, editorCallback, 'id=' + oArgs.editor.getRecord().getData('id') + '&field=' + field + '&newValue=' + oArgs.newData); }); }; |
From the code above we can see that the taglib creates a registerEditorListener function, and does indeed define a callback to be registered with the AJAX call. BUT it only defines what to do in case of a failure (line 5) and not what to do in cases of success. Knowing this, its easy enough to tweak the grails-app/taglib/DataTableTagLib.groovy file included in the GrailsUI plugin project to allow for us to pass in as part of the g:dataTable configuration a callback to use for cell editing. After tweaking, our augmented taglib generates something like this:
1 2 3 4 5 6 7 8 9 | ... var editorCallback = { success: function(o) { ${editorSuccessCallback}; }, failure: function(o) { ... } }; |
…and we simply add the editorSuccessCallback parameter to our g:dataTable configuration data:
1 2 3 4 5 | <gui:dataTable id="dt_1" ... editorSuccessCallback="GRAILSUI.dt_1.refresh();" .. ]"/> |
Now anytime we perform any successful cell edit (including deletes) , the DataTable.refresh() function is invoked and our table is rendered accurately. Killer.
Conclusion and Next Steps
Fundamentally the application is now functionally complete. There was plenty of mundane polishing and such that was not documented in this series, but all of the big stuff was touched upon.
The work accomplished in todays posts took a little over 20 hours to complete… the majority of the time split evenly between exploring the GrailsUI TagLib code trying to make the refresh work and tweaking CSS (which always seems to take more time than expected.) That brings our total time spent todate to about 30 hours.
The next post will (finally!) talk about deploying the twodo application into Amazon’s Elastic Compute Cloud (EC2). See you soon…
Technorati Tags: agile, grails, groovy, grailsui, java, software development
8 Comments currently posted.
bendanpa says:
ALTERthought Blogs » Grails and Cloud Computing: Part 1 – Amazon EC says:
[...] involved an experiment to see what we could build in about typical weeks worth of work. In the last post we effectively completed the basic functional requirements of the application, and now have a [...]
Ignacio Galmarino says:
I think some code is missing in your article !!!
Claes Svensson says:
Thank you kindest for an excellent walkthrough of Grails with YUI DataTable. This is the simplest solution I have seen for using YUI DataTable with server side support for handling changes. Great work!
Would you mind sharing the source code for twodo with us? It would be of great help to get a head start into this subject and maybe I can refine the solution further if I make plans to give it a go. I am keen on using DataTable for a project and have considered Grails also, so it would make a good match.
don says:
@Claes yes I keep meaning to make the code available for this series (as well as finish the last article on using GAE). I will endeavor to make both happen ASAP! Thanks for the kind words.
don says:
@Ignacio… I just now realized what your post was referring to… somehow an older revision of the post was accidentally published instead of the version that included more inline code samples! Hopefully this has been remedied now, and the post is clearer/more usable.
kai says:
hello thanks for such a great writeup and the topic is great. Will you be making your code available? It will serve as a very good tutorial for us beginner. Thanks :)
kai says:
hello i was trying out your demo but was stuck on the part where we need to refresh the table after inserting new task. The table didn’t refresh, did i miss out something?





Very nice, awaiting the next 2posts!