Contents


Give your Grails applications a facelift

Change your application's look and feel with CSS, templates, tag libraries, and more

Comments

Content series:

This content is part # of # in the series: Mastering Grails

Stay tuned for additional content in this series.

This content is part of the series:Mastering Grails

Stay tuned for additional content in this series.

Welcome to the second year of Mastering Grails. As I promised in the last article of 2008, with the new year comes a new application. Goodbye Trip Planner, hello blog publishing system.

I've named the application Blogito. It's either Spanish for "little blog" or an homage to Descartes' Cogito ergo sum ("I think, therefore I am"). You can download the finished application from blogito.org. Over the next couple of articles, you will build up the core functionality step by step.

In this article, the focus is on dramatically changing the look and feel of a Grails application. Last year's Trip Planner had a Frankenstein look and feel that only a developer could love. (To be fair, I was more interested in core functionality than aesthetics.) This time around, with a few CSS tweaks here and a couple of partial templates there, you'll end up with a Web application that looks nothing like an out-of-the-box Grails application. Along the way, you'll also get a brief refresher on Grails features like scaffolding, autotimestamping, modifying the default templates, creating a custom TagLib, and adjusting key configuration files such as Bootstrap.groovy and URLMapper.groovy.

But before you get started, you need to install Grails 1.1, still in beta as of this writing.

Installing Grails 1.1

Grails runs best on Java 1.5 or 1.6. Type java -version at a command prompt to confirm that you are up to date.

Once you have Java 1.5 or 1.6 in place, the steps for installing Grails are simple as always:

  1. Download grails.zip from the Grails site.
  2. Unzip grails.zip.
  3. Create a GRAILS_HOME environment variable.
  4. Add GRAILS_HOME/bin to the PATH.

If you have an application written in a previous version of Grails, you can type grails upgrade to migrate it to the current version. But what if you want to work with multiple versions of Grails?

If you are on a UNIX®-esque OS (UNIX, Linux®, or OS X), you can easily flip among Grails versions by pointing the $GRAILS_HOME environment variable to a symlink. On my system, GRAILS_HOME points to /opt/grails. Once that is in place, a quick ln -s moves me from release to release, as shown in Listing 1:

Listing 1. Creating a symlink for $GRAILS_HOME on UNIX, Linux, or Mac OS X
$ ln -s /opt/grails-1.1-beta1 grails

$ ls -l | grep "grails"
lrwxr-xr-x   1 sdavis  admin        17 Dec  5 11:12 grails -> grails-1.1-beta1/
drwxr-xr-x  14 sdavis  admin       476 Nov 10  2006 grails-0.3.1
drwxr-xr-x  16 sdavis  admin       544 Feb  9  2007 grails-0.4.1
drwxr-xr-x  17 sdavis  admin       578 Apr  6  2007 grails-0.4.2
drwxr-xr-x  17 sdavis  admin       578 Jun 15  2007 grails-0.5
drwxr-xr-x  19 sdavis  admin       646 Jul 30  2007 grails-0.5.6
drwxr-xr-x  18 sdavis  admin       612 Sep 18  2007 grails-0.6
drwxr-xr-x  19 sdavis  admin       646 Feb 19  2008 grails-1.0
drwxr-xr-x  18 sdavis  admin       612 Apr  5  2008 grails-1.0.2
drwxr-xr-x  18 sdavis  admin       612 Oct  9 21:46 grails-1.0.3
drwxr-xr-x  18 sdavis  admin       612 Nov 24 20:43 grails-1.0.4
drwxr-xr-x  18 sdavis  admin       612 Dec  5 11:13 grails-1.1-beta1

On a Windows® system, your best bet is to change the %GRAILS_HOME% variable directly. Don't forget to relaunch any existing command prompts after you make the change.

Type grails -version to confirm that you are using the latest release and that your GRAILS_HOME variable is being set correctly. Your output should look like Listing 2:

Listing 2. Output from grails -version
$ grails -version
Welcome to Grails 1.1-beta2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /opt/grails

Now that Grails 1.1 is installed, you are ready to create a new application.

Creating the application

Type grails create-app blogito to scaffold out the initial directory structure. Change to the new blogito directory and type grails create-domain-class Entry to create the class that will represent the blog entries. Look in grails-app/domain for Entry.groovy and add the code in Listing 3:

Listing 3. Creating the Entry class
class Entry {
  static constraints = {
    title()
    summary(maxSize:1000)
    dateCreated()
    lastUpdated()
  }
  
  String title
  String summary
  Date dateCreated
  Date lastUpdated
}

Each Entry has a title and summary field. Setting the maxSize constraint to 1,000 characters causes the dynamically scaffolded HTML forms to provide a text area for the summary field instead of a simple text field.

Remember that dateCreated and lastUpdated are magic field names in Grails. These timestamp fields are perfect for a blog application — they'll allow you to keep the latest Entry at the top of the list.

With the domain class in place, the next step is to create a controller. Type grails create-controller Entry. Add the code in Listing 4 to grails-app/controllers/EntryController.groovy:

Listing 4. Creating the EntryController
class EntryController {
    def scaffold = Entry
}

The deceptively simple def scaffold = Entry line instructs Grails to scaffold out the rest of the support for the Entry class. As you'll see in just a moment, you'll have an entry table with a column for each field in the Entry class (plus an ID field for the primary key and a version field for optimistic locking). You'll also have a full set of Groovy Server Pages (GSPs) that provide the mundane but all-important Create/Retrieve/Update/Delete (CRUD) capabilities.

Type grails run-app and visit http://localhost:8080/blogito in a Web browser. Click on EntryController, then New Entry. The good news is that all of the Entry fields show up in the create form (see Figure 1). That's also the bad news — those timestamp fields shouldn't be available for the user to mess around with. You need to adjust the default templates to fix this issue.

Figure 1. Editable timestamp fields in the Create Entry form
Editable timestamp fields in the Create Entry form
Editable timestamp fields in the Create Entry form

Adjusting the default templates

You could type grails generate-views Entry and manually delete the dateCreated and lastUpdated fields from the GSP files, but that cures the symptom instead of the underlying disease. Chances are you won't ever want those fields to show up in the create and edit forms. Your best bet is to change the templates behind def scaffold.

Type grails install-templates. Look in src/templates/scaffolding for create.gsp and edit.gsp. Add dateCreated and lastUpdated to excludedProps in each file, as shown in Listing 5:

Listing 5. Excluding the timestamp fields in the list.gsp and show.gsp templates
excludedProps = ['version',
                 'id',
                 'dateCreated',
                 'lastUpdated',
                 Events.ONLOAD_EVENT,
                 Events.BEFORE_DELETE_EVENT,
                 Events.BEFORE_INSERT_EVENT,
                 Events.BEFORE_UPDATE_EVENT]

Restart Grails and verify that the timestamp fields no longer appear (see Figure 2):

Figure 2. A form that excludes the timestamp fields
A form that excludes the timestamp fields
A form that excludes the timestamp fields

Changing the sort order

As you add new entries, you'll quickly see that tables are sorted by ID by default. Blogs typically sort their entries in reverse chronological order — newest first. In previous versions of Grails, changing the default sort order meant hand-editing the list closure in EntryController.groovy. Adding the two new sort lines below the existing max line isn't especially onerous (see Listing 6). The problem is this code is no longer being dynamically scaffolded behind the scenes. (You can look in src/templates/scaffolding/Controller.groovy or type grails generate-controller Entry to see the default underlying implementation.)

Listing 6. Sorting in Grails 1.0.x
def list = {
    if(!params.max) params.max = 10
    if(!params.sort) params.sort = "lastUpdated"
    if(!params.order) params.order = "desc"
    [ entryList: Entry.list( params ) ]
}

Grails 1.1 adds a simple but incredibly useful feature to the static mapping block — sort. Add the mapping block in Listing 7 to Entry.groovy. By handling sorting in the domain class, you can continue to def scaffold your controllers.

Listing 7. Adding sort to the static mapping block
class Entry {
  static constraints = {
    title()
    summary(maxSize:1000)
    dateCreated()
    lastUpdated()
  }
  
  static mapping = {
    sort "lastUpdated":"desc"
  }  
    
  String title
  String summary
  Date dateCreated
  Date lastUpdated
}

Restart Grails and verify that edited entries do indeed float to the top of the list, as shown in Figure 3:

Figure 3. Verifying the new sort order
Verifying the new sort order
Verifying the new sort order

Creating dummy records in development mode

Have you noticed that you lose your entries each time you restart Grails? Remember this is a feature, not a bug. The entry table is created every time you start Grails and dropped every time you shut down. Open grails-app/conf/DataSource.groovy to verify this. The db-create value for development mode is clearly set to create-drop.

You could change the value to update, but that isn't ideal either. Early in the development process, the schema is usually pretty fluid — you are constantly adding and removing fields, modifying the constraints, and so on. Until things settle down, I find it best to leave db-create set to create-drop.

To ease the tediousness of constantly retyping sample data in development mode, add a bit of logic to grails-app/conf/BootStrap.groovy. The code in Listing 8 inserts new records each time Grails starts up:

Listing 8. Adding dummy records in development mode
import grails.util.GrailsUtil

class BootStrap {

  def init = { servletContext ->
    switch(GrailsUtil.environment){
      case "development":
        new Entry(
          title:"Grails 1.1 beta is out", summary:"Check out the new features").save()
        new Entry(
          title:"Just Released - Groovy 1.6 beta 2", summary:"It is looking good.").save()
      break

      case "production":
      break
    }

  }
  def destroy = {
  }
}

Restart Grails once again. This time, you should be greeted with existing records in the entry table, as shown in Figure 4:

Figure 4. Dummy records that appear on boot
Dummy records that appear on boot
Dummy records that appear on boot

Beautifying the list

The default HTML table in the list view is fine for starters, but it clearly isn't a long-term solution for Blogito. Typically blog pages display the date, title, and summary fields vertically instead of horizontally, one entry at a time.

To make this change, type grails generate-views Entry. The GSP files that were previously being dynamically scaffolded should now appear in grails-app/views/entry. Open list.gsp in a text editor. Change the title in the header from Entry List to Blogito. Delete the <h1> and <g:if> blocks, and replace the existing <div class="list"> with the code in Listing 9:

Listing 9. Changing the list.gsp view
<div class="list">
 <g:each in="${entryInstanceList}" status="i" var="entryInstance">
  <div class="entry">
   <span class="entry-date">${entryInstance.lastUpdated}</span>
   <h2><g:link action="show" id="${entryInstance.id}">${entryInstance.title}</g:link></h2>
   <p>${entryInstance.summary}</p>                
  </div>  
 </g:each>
</div>

Notice that the code is dramatically simplified. The <fieldValue> tags can be removed — they help bind domain class fields to HTML form fields, but they don't add any real value here. Each Entry is wrapped in a named <div>, and the lastUpdated field is wrapped in a named <span>. The class attributes give you a hook for the CSS formatting you'll do in just a moment. The title and summary fields are wrapped in plain old HTML heading and paragraph tags.

Refresh the list view in your browser (see Figure 5). At this point, it's hard to call this an improvement. But with a few new CSS instructions, things should be markedly better looking.

Figure 5. The new list without CSS
The new list without CSS
The new list without CSS

Add the CSS in Listing 10 to the bottom of web-app/css/main.css:

Listing 10. CSS customizations for the list.gsp view
/* Blogito customizations */
.entry {
  padding-bottom: 2em;
}

.entry-date {
  color: #999;
}

Refresh your Web browser again and things should look a bit more stylish (see Figure 6). You're not done with the CSS yet, but you are off to a good start.

Figure 6. The new list with CSS
The new list with CSS
The new list with CSS

Creating a Date TagLib

It's time to take a stab at making the lastUpdated date look a little more user-friendly. The best place to put reusable snippets of code is in a custom TagLib. Type grails create-tag-lib Date. Add the code in Listing 11 to grails-app/taglib/DateTagLib.groovy:

Listing 11. Code for DateTagLib
import java.text.SimpleDateFormat

class DateTagLib {
  def longDate = {attrs, body ->
    //parse the incoming date
    def b = attrs.body ?: body()
    def d = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(b)
        
    //if no format attribute is supplied, use this
    def pattern = attrs["format"] ?: "EEEE, MMM d, yyyy"
    out << new SimpleDateFormat(pattern).format(d)
  }
}

Now wrap the lastUpdated field in grails-app/views/entry/list.gsp in your newly created <g:longDate> tag, as shown in Listing 12:

Listing 12. Using the <g:longDate> tag in list.gsp
<div class="entry">
  <span class="entry-date"><g:longDate>${entryInstance.lastUpdated}</g:longDate></span>
  <h2>${entryInstance.title}</h2>                  
  <p>${entryInstance.summary}</p>                
</div>

Restart Grails and refresh your Web browser. You should see the reformatted dates appear, as in Figure 7:

Figure 7. Newly formatted dates using the custom <g:longDate> tag
Newly formatted dates using the custom <g:longDate> tag
Newly formatted dates using the custom <g:longDate> tag

Creating a partial template

That layout is looking pretty good. I'd like to reuse it for show.gsp as well. Create _entry.gsp in grails-app/views/entry and add the code in Listing 13. (Of course, you can simply cut and paste it from list.gsp.)

Listing 13. The code for _entry.gsp
<div class="entry">
 <span class="entry-date"><g:longDate>${entryInstance.lastUpdated}</g:longDate></span>
 <h2><g:link action="show" id="${entryInstance.id}">${entryInstance.title}</g:link></h2>
 <p>${entryInstance.summary}</p>
</div>

To use your newly created partial template, adjust list.gsp as shown in Listing 14:

Listing 14. Using the _entry.gsp partial template in list.gsp
<div class="list">
  <g:each in="${entryInstanceList}" status="i" var="entryInstance">
    <g:render template="entry" bean="${entryInstance}" var="entryInstance" />
  </g:each>
</div>

Now you can reuse the partial template in show.gsp as well, as in Listing 15:

Listing 15. Using the _entry.gsp partial template in show.gsp
<div class="body">
  <g:render template="entry" bean="${entryInstance}" var="entryInstance" />
  <div class="buttons">
    <!-- snip -->
  </div>
</div>

Refresh the list view in your browser. It should look exactly the way it did before. Now click on an entry's title to verify that it works for the show view as well.

Customizing the header

Things are beginning to pull together nicely. Now it's time to replace the Grails branding with your own.

I don't see the Grails logo referenced anywhere in list.gsp or show.gsp. Remember that Grails uses SiteMesh to pull the various parts of the finished page together. Look in grails-app/views/layouts/main.gsp and you'll see where the grails_logo.jpg file gets included.

Create another partial template named _header.gsp in grails-app/views/layouts. Add the code in Listing 16. Notice that Blogito is a hyperlink back to the main page.

Listing 16. Code for the _header.gsp partial template
<div id="header">
  <p><g:link class="header-main" controller="entry">Blogito</g:link></p>
  <p class="header-sub">A tiny little blog</p>
</div>

Now edit main.gsp as shown in Listing 17 to include the _header.gsp file:

Listing 17. Main.gsp using the new _header.gsp partial template
<body>
  <div id="spinner" class="spinner" style="display:none;">
    <img src="${createLinkTo(dir:'images',file:'spinner.gif')}" alt="Spinner" />
  </div> 
  <g:render template="/layouts/header"/>        
  <g:layoutBody />  
</body>

Finally, add a bit more to web-app/css/main.css, as shown in Listing 18:

Listing 18. CSS formatting for the _header.gsp partial template
#header {
  background: #67c;
  padding: 2em 1em 2em 1em;
  margin-bottom: 1em;
}

a.header-main:link, a.header-main:visited {
  color: #fff;
  font-size: 3em;
  font-weight: bold;
}

.header-sub {
  color: #fff;
  font-size: 1.25em;
  font-style: italic;
}

Refresh your browser to see the changes (see Figure 8). Click on an entry's title, and then click on Blogito in the header to navigate back to the main page.

Figure 8. Showing off your new header
Showing off your new header
Showing off your new header

Hiding the navigation bar unless you are logged in

You need to deal with one more tell-tale sign that this is a Grails application: the navigation bar. Although you won't implement authentication until the next article, you can turn off the navigation bar for unauthenticated users right now by wrapping the <div> in a simple <g:if> test. This test is looking for a user variable stored in session scope.

Modify both list.gsp and show.gsp as shown in Listing 19:

Listing 19. Hiding the navigation bar unless you are logged in
<g:if test="${session.user}">
  <div class="nav">
      <span class="menuButton">
         <a class="home" href="${createLinkTo(dir:'')}">Home</a>
      </span>
      <span class="menuButton">
         <g:link class="create" action="create">New Entry</g:link>
      </span>
  </div>
</g:if>

In show.gsp, add the same test around the buttons <div>. (The last thing you want is unauthenticated users editing or deleting your blog entries, right?)

Finally, make one more aesthetic tweak to list.gsp. Move the paginateButtons <div> outside of the body <div>, as shown in Listing 20. This allows the bar to stretch across the entire screen, adding a nice visual anchor to the bottom of the screen.

Listing 20. Moving the paginateButtons <div> outside the body <div> for aesthetics
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <meta name="layout" content="main" />
  <title>Blogito</title>
 </head>
 <body>
  <g:if test="${session.user}">
   <div class="nav">
    <span class="menuButton">
      <a class="home" href="${createLinkTo(dir:'')}">Home</a>
    </span>
    <span class="menuButton">
      <g:link class="create" action="create">New Entry</g:link>
    </span>
   </div>
  </g:if>
   <div class="body">
    <div class="list">
     <g:each in="${entryInstanceList}" status="i" var="entryInstance">
      <g:render template="entry" bean="${entryInstance}" var="entryInstance" />
     </g:each>
    </div>
   </div>    
   <div class="paginateButtons">
    <g:paginate total="${Entry.count()}" />
   </div>
 </body>
</html>

Add one more bit of CSS, shown in Listing 21, to ensure that the paginateButtons <div> shows up below the body <div> instead of next to it:

Listing 21. CSS to ensure that the paginateButtons <div> appears at the bottom of the screen
.paginateButtons{
  clear: left;
}

Refresh your browser for the last time. Your screen should look like Figure 9:

Figure 9. Hiding the navigation bar
Hiding the navigation bar
Hiding the navigation bar

Setting the home page

Now that everything is in place, you should make EntryController the default home page. You accomplish this by adding a mapping that redirects / (the trailing slash on the URL http://localhost:9090/blogito/) to the EntryController. Edit grails-app/conf/UrlMappings.groovy to match Listing 22:

Listing 22. Making EntryController the default home page
class UrlMappings {
    static mappings = {
      "/$controller/$action?/$id?"{
            constraints {
                   // apply constraints here
              }
        }
        "/"(controller:"entry")
        "500"(view:'/error')
   }
}

Conclusion

This article's goal was to show you how to give your Grails application a facelift. With a few lines of CSS, you can change colors, fonts, and the spacing around block elements. Through partial templates and TagLibs, you can create some reusable snippets of code. In the end, you have all the benefits of the Grails framework and an application that has its very own look and feel.

In the next installment, you'll continue to flesh out the Blogito application. You'll add a User domain class so that multiple people can add blog entries. You'll also explore Grails codecs and play around a bit more with custom URL mappings. Don't forget that you can download the completed application at http://blogito.org. Until then, have fun mastering Grails.


Downloadable resources


Related topics

  • Mastering Grails: Read more in this series to gain a further understanding of Grails and all you can do with it.
  • Grails: Visit the Grails Web site.
  • Grails Framework Reference Documentation: The Grails bible.
  • "Design Web pages with class" (Molly Holzschlag, developerWorks, September 1999): Find out two ways to use classes with stylesheets to make quick work of designing (or redesigning) your HTML documents.
  • Care With Font Size: Check out this tip in the W3C Quality Assurance series.
  • Box Model: Learn more about padding and margin in CSS.
  • Groovy Recipes (Scott Davis, Pragmatic Programmers, 2007): Learn more about Groovy and Grails in Scott Davis' latest book.
  • Practically Groovy: This developerWorks series is dedicated to exploring the practical uses of Groovy and teaching you when and how to apply them successfully.
  • Groovy: Learn more about Groovy at the project Web site.
  • AboutGroovy.com: Keep up with the latest Groovy news and article links.
  • Grails: Download the latest Grails release.

Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java development, Web development, Open source
ArticleID=365451
ArticleTitle=Mastering Grails: Give your Grails applications a facelift
publish-date=01202009