Tuesday, November 10, 2009

Ajax-Enabled Checkboxes in Grails

In Grails: A Quick-Start Guide, we have a task list to help TekEvent organizers and volunteers keep track of what's left to be done for their event. It's pretty handy, but it could be a bit easier to use.

Specifically, it would be great if we didn't have to click on a task to open the show view and then click again to edit it, just to mark a task as completed. What would be really nifty is if we could mark a task as completed by clicking on a simple checkbox. And it turns out that Grails provides an Ajax tag - <g:remotefunction> - that's just what we need.

Here's a picture of our current list view. We'll put our checkbox in the last column, since we don't really need to repeat the Event name.



We'll modify the list view in two places. First, in the <head> section, we'll replace this:

<tr>

<g:sortableColumn property="id" title="Id" />

<g:sortableColumn property="title" title="Title" />

<g:sortableColumn property="notes" title="Notes" />

<th>Assigned To</th>

<g:sortableColumn property="dueDate" title="Due Date" />

<th>Event</th>

</tr>

With this:

<tr>

<g:sortableColumn property="id" title="Id" />

<g:sortableColumn property="title" title="Title" />

<g:sortableColumn property="notes" title="Notes" />

<th>Assigned To</th>

<g:sortableColumn property="dueDate" title="Due Date" />

<th>Completed</th>

</tr>

All we did here is change the text of the column heading. (Trivial, yet necessary.)

Next, we will modify the table row that currently displays the Event name. Let's replace this:

<td>${fieldValue(bean:taskInstance, field:'event')}</td>

with this:

<td>

<g:checkBox name='completed' value="${taskInstance.completed}"
onclick="${remoteFunction(action:'toggleCompleted', id:taskInstance.id,
params:'\'completed=\' + this.checked')}" />
</td>

Here we are using a <g:checkBox> tag to show whether a Task is completed or not. We will also use it to toggle the completed value. For that, we use a tag, but we call it as a method and assign it to the onclick event of the checkBox. We'll talk a bit more about the remoteFunction, but first, there's one more thing we need.

In order to use any of Grails' JavaScript tags, we need to set a JavaScript library to be used. We do this with the <g:javascript> tag, which can be placed anywhere in the <head> section of our page. Like so:

<g:javascript library=”prototype” />

The remoteFunction tag can take a controller, action, id and params. We will use all except the controller, since we are on a page produced by the TaskController, which is the controller we'll be using. This tag can also take an update attribute, but we don't want to update anything on the page, so we'll skip that one too. For the action we'll use 'toggleCompleted' (which we will write shortly). The id will be the id of the taskInstance being displayed. Then comes the params.

The params property can be a Map just as in other GSP tags, but it can also be a String formatted like request parameters. That's the way we use it here: params:'\'completed=\' + this.checked'. Since we are already inside a double quoted GString, we have to use single quotes for the params value, and that means escaping the single quotes that need to appear in the final value. The phrase “this.checked” is a literal that will be passed into the resulting JavaScript function, where it will be evaluated to the checked value of the checkbox. This value will end up in our controller as either 'true' or 'false'.

If we were to look at the page with Firebug (or a similar tool) we would see that the value being assigned to the onclick event is something like this:

new Ajax.Request('/TekDays/task/toggleCompleted/1',{asynchronous:true,evalScripts:true,parameters:'completed=' + this.checked});

Here's what our page looks like now:



As I mentioned earlier, we don't need to update anything on the page when the user clicks the checkbox. The checkbox already gives the visual cue we are looking for. What we want is to set the completed property on the corresponding Task instance and then save it. So, let's implement the toggleCompleted action in our TaskController. Open /grails-app/controllers/TaskController.groovy and add the following action:

def toggleCompleted = {
def task = Task.get(params.id)
if (task){
task.completed = params.completed == 'true'
task.save()
}
render ''
}

The first step in this action is to get the Task based on the id passed in. Then if we get that successfully, we'll set its completed property based on the value of the completed parameter. You might be tempted to try task.completed = params.completed, but params.completed is a String (either 'true' or 'false') and not a Boolean value. So we have to compare against a String literal.

Next we save the Task instance, and finally render a blank String to prevent any attempt to render something back to the browser.

That's all there is to it. We can now mark a task complete or incomplete right from the list view - no form needed. Pretty handy.

(Now we could also clean up the page in other ways, as we did on our TekEvent list view in section 6.2, “Modifying the Scaffolded Views,” on page 92.)

Wednesday, October 28, 2009

Grails: A Quick-Start Guide is shipping!

Last week, during the SpringOne 2GX conference, I received word that pragprog.com was shipping pre-orders of GQuick. Then just today, I began hearing from visionary individuals that their Amazon.com pre-orders had arrived!

A huge Thank You to all of those who pre-ordered or who got the beta ebook. Feedback is always welcome and if you have questions or would like to see something expanded on here on the GQuick blog, drop me a line or post to the PragProg forum at http://forums.pragprog.com/forums/116

Next up, we will see how we can add some helpful features to the Task List in the "Getting Things Done" chapter. Thanks to beta reader, Johannes Deutschland for that suggestion.

Then, since Grails 1.2 is destined to be out soon, I will be working through the code to see if anything breaks in 1.2. If so, I'll post the fixes here.

Wednesday, August 26, 2009

Testing GSP Custom Tags

In Grails: A Quick-Start Guide we used several custom tags. Unfortunately we didn't get a chance to write any tests for these tags. So let's do that now.

In GQuick we have a loginToggle tag which will display a "login" link if no user is logged in and a "logout" link if a user is logged in.


  def loginToggle = {

    out << "<div>"

    if (session.user){

      out << "<span>"

      out << "Welcome ${session.user}."

      out << "</span><span>"

      out << "<a href='${createLink(controller:'tekUser',  

                         action:'logout')}'>"

      out << "Logout </a></span>"

    }

    else{

      out << "<span>"

      out << "<a href='${createLink(controller:'tekUser',

                         action:'login')}'>"

      out << "Login </a></span>"

    }

    out << "</div><br/>"

  }




As you can see from the tag code, we are storing a reference to our user object in the session. If we find a user we display a welcome message and a Logout link. If there is no user we just display the Login link. Let see how we can write a unit test for this tag.

When we create a taglib with the grails create-tag-lib command, a corresponding test is created for us which extends the TagLibUnitTestCase class. This class makes testing taglibs a piece of cake. Take a look at the following test:


    void testLoginToggleWithUser() {

        mockSession.user = "user"

        def output = tagLib.loginToggle().toString()

        assert output.contains('Logout')

        assert output.contains('action=logout')

    }




The first thing you'll notice is that we are setting the value of user on the mockSession object. This is one of the mock objects that are included in the TagLibUnitTestCase. Also included are mockRequest, mockResponse, mockParams, and mockFlash. Any calls to session in our tag code will be directed to this mockSession. So, when we look for the existence of a user to determine which text to display, we will find one.

In the next line we call the tag as a method of the implicit tagLib object. This object is loaded for us by the TagLibUnitTestCase. We don't need to declare or create it. It's just there and ready for us to use. Every tag in the tagLib can be called as a method. All of these tag methods return an instance of java.io.StringWriter. We call toString() on this to get a String representation of what the tag would have sent to the page.

Next we just makes some assertions on that output. We know that, if a user is in the session, we should have the word 'Logout' in the tag's output, so we can check for that with the contains() method. We'll talk about the second assertion in a minute. But first, if we were to run this test it would fail with an error because out tag calls the createLink tag that comes with Grails and none of the built-in Grails tags are available to unit tests.

Groovy makes this easy to handle. Let's just add the following line to our test class' setUp() method.

tagLib.metaClass.createLink = {map -> "action=${map.action}"}

Make sure to add this after the call to super.setUp() so that the tagLib object will be there. Now when a call to createLink is made within our tagLib it will call the method on the tagLib.metaClass which will return a string with the name of the action that the createLink would have linked to. This allows us to verify that we are passing the correct action, in this case 'logout'.

To be thorough, we'll also write a test to verify the behavior of our tag when there is no user logged in.

    void testLoginToggleNoUser() {

        def output = tagLib.loginToggle().toString()

        assert output.contains('Login')

        assert output.contains('action=login')

    }







Now that we see how easy it is to write unit tests for Grails tagLibs we have no excuse for leaving those auto-generated test classes empty.

Saturday, August 15, 2009

Display An Image Property In a GSP

This is the first post for the GQuick blog. In this blog I plan to include helpful tips about Grails that we weren't able to get to in the book as well as address questions that come up from readers.

To kick this off we'll look at something that a couple readers of the beta ebook have asked about: Displaying the Sponsor logo in a GSP.

For those who haven't read the book, a bit of background. We have a Sponsor class which represents organizations that might sponsor a technical event. Here's an abbreviated listing of the Sponsor class:
class Sponsor {
...
byte[] logo

static constraints = {
...
logo(nullable:true, maxSize:1000000)
}
}

This will give us, in most databases, a BLOB type in which we can store an image of our sponsor's logo. Grails' scaffolded views provide the file upload mechanism for us but they don't provide a way to display the stored image in our pages. But don't worry, it's a piece of cake, this is Grails after all.

The first thing we'll do is add an action to our SponsorController called displayLogo.
class SponsorController {
...

def displayLogo = {
def sponsor = Sponsor.get(params.id)
response.contentType = "image/jpeg"
response.contentLength = sponsor?.logo.length
response.outputStream.write(sponsor?.logo)
}
}

In this action we are retrieving a Sponsor with the id being passed in the params. Then we set a couple properties on the response object that is automatically injected into all Grails controllers. We set the contentType to "image" and the contentLength to the length of the logo property of the Sponsor instance. Finally we write the sponsor.logo to the outputStream of the response. Pretty simple so far.

The next step is to call this action in our GSP. To do this we'll use the createLink tag within an img tag, like so:

<img src="http://www.blogger.com/${createLink(action:'displayLogo', id:sponsorInstance?.id)}" />

Now if we drop this in our sponsor/show.gsp in place of the current ${sponsorInstance?.logo} (which spits out a bunch of binary goodness) we will get... an x or a ? depending on which browser we're using. Don't panic, this is just our SecurityFilter trying to help us out. ( For those who haven't read GQuick, we have a SecurityFilter that checks for a logged in user for all actions but those listed in the allowedActions list).

So, let's add displayLogo to the allowedActions list, like this:

def allowedActions = ['show', 'index', 'list', 'login',
                      'validate', 'search', 'displayLogo']
Note: this step is only necessary if you've been following along with the TekDays application in Grails: A Quick-Start Guide. If you are just using this technique in your own application then you're already done.

Now when we view one of our sponsors it will look more like this: