ALTERthought Blogs

17 August 2009

Grails App in ~40 hrs (part 4)

The last post covered the kickoff to the ‘twodo‘ application– where we created our Grails application, wired in JSecurity via plugin, and created a few of the core Domain objects the system will need.

In this post we will refine the general look and feel of the User Interface, as well as start building the core list management functionality via a UI Plugin.

Look and Feel

To this point we have done almost no UI creation, and have not customized the default out-of-the-box look and feel of the application. I knew from talking to the stakeholder (in this case my business partner) that he envisioned a simple/clean interface with little clutter. The general site template was envisioned to look something like this:

layout

Under the assumption that the look and feel, would (like all other aspects of the app) continue to evolve over the course of the development iterations, I did not want to spend too much time on polishing the initial cut at the UI template. I started with a simple raw HTML/CSS template, a bit of time in The Gimp and some CSS tweaking via Firebug until I got something that seemed initially acceptable. I added in some greeting text and voila– comparing the original/old login screen:

Crude Login Screen

…and the new one:
Picture 1

OK. Still not award winning perhaps, but at least it looks intentional.

Its worth noting that the fancy rounded borders around the login/register boxes were quickly done using CSS3 tags. If the stakeholder likes the look, we can make something more permanent/portable. If they dont like it, we have not spent a load of time on something that gets thrown away

I don’t profess to be much an artist when it comes to web pages, and don’t intend to turn this post into a treatise on web design, but I think its worth peeking at the main views/layout/main.gsp layout file used for this project:

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
<html>
   <head>
        <title><g:layoutTitle default="TwoDo" /></title>
        <link rel="stylesheet" href="${createLinkTo(dir:'css',file:'template_css.css')}" type="text/css"/>
        <link rel="stylesheet" href="${createLinkTo(dir:'css',file:'main.css')}" type="text/css"/>
        <link rel="shortcut icon" href="${resource(dir:'images',file:'favicon.ico')}" type="image/x-icon" />
 
        <g:javascript library="application" />
        <g:layoutHead />
  </head>
  <body>
    <g:render template="/layouts/includes/logo"/>
    <div id="page">
      <div id="page-bg">
        <g:render template="/layouts/includes/messages"/>
        <g:render template="/layouts/includes/errors"/>
 
        <!-- start content -->
        <div id="content">
          <g:layoutBody/> 
        </div>
        <!-- end content -->
        <div style="clear:both;">&nbsp;</div>
      </div>
    </div>
    <g:render template="/layouts/includes/footer"/>
 </body>
</html>

The main thing that is worth noting is that for every major rectangular region of the main template (as indicated on the napkin) we have further subdivided the layout into smaller templates which are then rendered from within main layout via the g:render taglib. As an example, we examine a few of the templates below.

Here is the layouts/includes/_logo.gsp template, which has been configured to:

<div id="logo">
  <h1><a href="${resource(dir:'')}">TwoDo!</a></h1>
<jsec:isLoggedIn>
  <span class="greeting" >
    Hello,<jsec:principal/> (<g:link controller="auth" action="signOut">logout</g:link>)</span>
</jsec:isLoggedIn>
</div>

And finally, a look at the layouts/includes/_messages.gsp which includes logic for the rendering of flash messages in a consistent manner:

<div id="message_wrapper" <g:if test="${!flash.message}"> style="display:none;"</g:if>>
  <div id="messageBar" <g:if test="${!flash.message}">style="display:none;"</g:if>>${flash.message}</div>
</div>

This divide-and-conquer strategy makes sense in many things software related, and the case of layout templates is no exception.

Security and Profile Management

It quickly becomes apparent that we require a mechanism for retrieving the current users Profile after a successful login. JSecurity provides a SecurityUtil class with a method that allows us to get the currently logged in Subject. From there we can get the Principle, and from the Principle we can get the Profile. I found it convenient to implement this logic in a pseudo-GORM like fashion by simply adding a static method to the existing Profile class:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
import org.jsecurity.SecurityUtils
 
class Profile extends Base {
  ...
  /**
    * Return the profile associated with the currently logged in user
    */
  static Profile current() {
    def principle = SecurityUtils.getSubject().getPrincipal()
    return Profile.findByUsername( principle )
  }
}

Our controllers/whatever now simply need to call Profile.current() to get the currently logged in user.

GrailsUI Plugin

As per our sitemap in the last post, we only have a handful of pages in the entire application. Once a user logs in, they will be at their home (views/home/index.gsp) page, which will display their current list of tasks side by side. The manipulations of these lists seemed a prime candidate for some fancy UI and AJAXy goodness. In particular I envisioned a dynamic table that would use AJAX to manipulate the contents of the task lists. Rather than roll it all from scratch we will enlist the help of a the Yahoo UI libraries by way of the GrailsUI plugin. I had used the YUI library on prior projects and, wanting to maximize my productivity, so chose it over other competing frameworks (jQuery, etc.)

The GrailsUI plugin basically provides us with a wrapper taglib that encapsulates the underlying YUI javascript. In particular we are interested in the gui:dataTable tag, which wraps a YUI DataTable Control. Installing the GrailsUI Plugin automagically installs its YUI dependencies.

Before we tackle using the taglib/control however, lets look ahead, and address our data testing needs.

From reading the associated documentation, I knew that our GrailsUI DataTable was eventually going be populated by an AJAX call that will return its data in JSON format– which is all well and good. So the first order of business was to create some fixture data with which to test. To this end I added a simple action to the default Task controller we generated in the last post to create a few tasks for purposes of testing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.alterlabs.twodo
 
class TaskController {
...
  def data = {
    def p = Profile.current()
    for ( i in 0..5 ) {
      p.addToWorkTasks(new Task(description:"Work Task #${i}", estimate:60))
      p.addToPersonalTasks(new Task(description:"Personal Task #${i}", estimate:60))
    } 
    flash.message = p.save()?"Added fake tasks to profile":"Failed to add fake tasks"
    redirect(action:list)
  }
...
}

The code above grabs the Profile of the current user then creates and associates some dummy Tasks with it. Pointing our browser at http://localhost:8080/blogtwodo/task/data invokes the action and allows us to visually verify the data by listing all the Tasks in the system.

OK. Now that we have some data, lets work on making a DataTable retrieve it via AJAX. The plugin home page has some helpful information about using the DataTable, as does the authors blog. As per usual I started with a crude test to work the kinks out, with the assumption that it could be suitably refined later. By following the examples provided in the plugin documentation I quickly put together a simple test page that looked like so:

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
<html>
  <head>
    <gui:resources components="['dataTable']" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="layout" content="main" />
    <title>DataTable Test</title>
  </head>
  <body>
    <div>  
      <h1>Work DataTable:</h1> 
      <gui:dataTable id="dt_1"
         columnDefs="[
            [id:'ID', formatter:'text', hidden:true],
            [status:'Status', formatter:'text'],
            [description:'Description', formatter:'text'],
            [est:'Est', formatter:'text'],
         ]"
         sortedBy="id"
         allowExclusiveSort='true'
         draggableColumns="false"
         controller="home" action="tasks" params="[taskType:'work']"
         rowsPerPage="10" 
         paginatorConfig="[
             template:'{PreviousPageLink} {NextPageLink} {CurrentPageReport}',
             pageReportTemplate:'{totalRecords} total records'
         ]"/>
    </div>
  </body>
</html>

In a nutshell, the above code:

  1. Includes the resources for the GrailsUI DataTable (line:3)
  2. Makes a DataTable with 4 columns (‘id’, ’status’, ‘description’ and ‘est’). (lines:11-17)
  3. Note: The ‘id’ column is strictly for sorting purposes and we don’t want it shown on the screen, so we mark it as ‘hidden’. (line:13)
  4. Wires the DataTable to populate itself from the action ‘tasks‘ on the ‘home‘ controller and also passes the parameter taskType set to ‘work‘ (line:21)

The other configuration attributes are reasonably self explanatory, or will get additional treatment in later posts. The g:dataTable tag does all the (considerable) heavy lifting for us and imports all the UI javascript files, configures the underlying YUI DataSource and associated response schema, as well as setting up various AJAX callbacks on our behalf. Great stuff.

We have configured our datatable tag to point at a ‘tasks‘ action, so lets take a look at that now. Its pretty basic:

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
import grails.converters.JSON
 
class HomeController {
 ... 
  def tasks = {
    def list = []
    //
    // Get the current user profile
    // 
    def profileInstance = Profile.current()
    def tasks = (params?.taskType == 'work')?profileInstance.workTasks:profileInstance.personalTasks
 
    tasks.each {
       list << [
           id: it.id,
           status: it.completionDate?"done":"front burner",
           est: it.estimate,
           description: it.description,
       ]
    }
    def data = [
      totalRecords: list.size(),
      results: list
    ]
    render data as JSON
  }
  ...
}

The GrailsUI DataTable expects its response schema to be fed to it in JSON format. Our tasks action basically:

  1. Gets the current Profile the logged in user (line:11 )
  2. Looks at the params to see which list of tasks to work on (line:12 )
  3. Iterates over the task list, adding a map of the Task attributes to a list (lines:14-20
  4. Create a Map that holds the new list, as well as the total number of records. This is required by the response schema (lines:22-25)
  5. Render the Map as JSON back to our view (line:26)

Pointing our browser to http://localhost:8080/blogtwodo/home/datatable should get us the following:
DataTable

OK, it may not look like much now, but it proves out the underpinnings. We will use it as a starting point, and continue to refine it in later posts.

ONE OTHER NOTE: Its mentioned in the GrailsUI documentation, but its worth mentioning again. If you want to make use of the default YUI CSS formatting (which we do) one needs to make sure that the HTML tag belongs to the CSS class yui-skin-sam. In our case this was added to the master layout file views/layouts/main.gsp

Next Steps

Alright. That’s about it for this post. To recap we have:

The above took about 1/2 a day, or 5 hours or so to make happen (most of which was spent wrangling with HTML & CSS,) which puts our running total of ~7 hours spent so far with ~33 remaining in the kitty.

In the next post we will continue to refine the overall UI and get our DataTable control to allow cell editing of our tasks. See you then!

Technorati Tags: , , , , ,

Post to Twitter Post to Delicious Post to Digg Post to Reddit

5 Comments currently posted.

bendanpa says:

Very good post. I am interested in how you put the app onto app-engine. Expecting some details about that.

thanks for the sharing,
Bendanpa

don says:

@bendanpa Thanks for the kind words. Keep checking back/subscribe… the app-engine post is about 2-3 away

RafaƂ says:

Hi, nice job!
Is there any chance to download your code?

don says:

@Rafal Yes, I plan on making the code available at the end of the series. Stay tuned…

ALTERthought Blogs » Grails App in ~40 hrs (part 5) says:

[...] Part 4: The UI is refined, and we start using the GrailsUI plugin to populate a YUI DataTable via AJAX. [...]

Post a comment on this entry: