Skip to content


Creating mobile web applications with jQuery mobile and Grails

With the announcement of an alpha release of jQuery Mobile, I decided to check it out and see how easy it would be to develop a web application that is optimized for mobile/tablet devices.

If you head over to the documentation site, the first thing you will notice is that the docs are actually built using jQuery mobile, which is quite cool since it gives you a quick feel of what to expect from the library. After skimming through the docs and building some sample pages, here are some of the things that I really liked about it (along with the other advertised features of course)

  • Markup driven (you can build a compile mobile web app with no javascript code at all)
  • Theming capability
  • Cool looking list views :)

After realizing how easy it is to prototype a mobile web app with jQuery mobile and how cool the rendered pages look out of the box, a crazy voice started whispering in my head (it was mainly asking a question really…), “How cool would it be to prototype mobile web apps using grails’s scaffolding?”
And my answer was “Wayyy Cool”, which is why I started my terminal and created a new grails application to see how I can achieve this.

Okay, so I knew in order to change the default scaffolded views generated by grails I needed to first install the templates (which are not found by default when creating an application). If you have run into a situation requiring you to edit the web.xml file you have probably done this step before (installing the templates). So within my newly created grails application I issued the following command

grails install-templates

This will create a number of files in your application’s src/templates folder including the ones responsible for populating your grails artifacts when issuing commands like grails create-controller etc… as well the templates responsible for scaffolding, which is what interests me for this example.
You will find that within the src/templates/scaffolding directory there are files conveniently named according to the view file names you get when you generate views using the grails generate-views command, you will also find a Controller.groovy which is responsible for generating controllers with the grails generate-controller command , and a file called renderEditor.template which is responsible for rendering the different form elements in the create and edit pages.

So for the purpose of my test, I will be editing list.gsp, show.gsp, create.gsp and edit.gsp and changing them to generate jQuery mobile markup. Also before I edit those file I will clean up the main template (main.gsp in grails-app/views/layout) to include the jQuery mobile css and javascript files to be used in all pages.

<!DOCTYPE html> 
<html>
  <head>
    <link rel="stylesheet" href="${resource(dir:'css',file:'jquery.mobile-1.0a1.min.css')}"/>
    <link rel="stylesheet" href="${resource(dir:'css',file:'main.css')}" type="text/css" media="all"/>
    <script src="${resource(dir:'js',file:'jquery-1.4.3.min.js')}" /></script>
    <script src="${resource(dir:'js',file:'jquery.mobile-1.0a1.min.js')}"></script>
    <title><g:layoutTitle default="Grails" /></title>
    <g:layoutHead />
  </head>
  <body>
    <g:layoutBody />
  </body>
</html>

Now that I have the required javascript and css, I will start by showing a sample of the edits I made for the list.gsp template, you can check out the rest of the files I’ve edited from the attached sample source code (at the bottom of the page).

jQuery mobile page basics

According to the docs, a jQuery mobile page with a header, content and footer will typically look something like this (inside the main body tag)

<div data-role="page"> 
	<div data-role="header">...</div> 
	<div data-role="content">...</div> 
	<div data-role="footer">...</div> 
</div> 

You will notice the use the attribute data-role to indicate the role of each div. There are other attributes used by jQuery mobile which will control themes, icons and their locations, button grouping etc… which I will run into later on.

Having defined the skeleton of each page, the first and simplest page template to edit is the list.gsp

Mobil-izing the list template

I want my list page to have the following attributes

  • A Header showing
    • Title of the domain class list being shown
    • Button to take me back to the home (index) page
  • The content will list the domain classes in my db showing the toString() representation of the domain object (to keep things simple I will not show a table like the default behavior, also it looks way cooler this way)
  • A footer showing
    • A button to create a new instance of the domain
    • Pagination buttons

Here is how the list template is broken up after I’ve edited it (all the divs below are part of the parent div with data-role=”page”)

Header

<div data-role="header" data-nobackbtn="true" data-position="fixed">
  <a href="\${createLink(uri: '/')}" data-icon="arrow-l">
    <g:message code="default.nav.home.label" default="Home" />
  </a>			
  <h1><g:message code="default.list.label" args="[entityName]" /></h1>
</div>

One important thing to note here, is that jQuery mobile can automatically add back button support to your application’s headers which is quite cool, but I preferred to add my own back buttons instead of the automatically created ones, hence you can see the data-nobackbtn=”true” attribute instructing the framework not to add the button for me, also I’ve added the attribute data-position=”fixed” which makes my header follow and stay at the top of the page when I scroll down a long page. (The current behavior is a bit buggy, but it looks like it is being worked on), the rest should be clear if you’ve written any gsp code before.

Content

<div data-role="content">
  <g:if test="\${flash.message}">
    <div class="message"><p>\${flash.message}</p></div>
  </g:if>
  <ul data-role="listview" >
    <g:each in="\${${propertyName}List}" status="i" var="${propertyName}">
      <li>
        <g:link action="show" id="\${${propertyName}.id}">\${${propertyName}}</g:link>
      </li>
    </g:each>
  </ul>
</div>

The content will first show any message that is in the flash scope (like the ones you get after deleting an object for example), below that is an unordered list which has the data-role=”listview” attribute which instructs the framework to create a, well…, ListView :) Within the list view I just use the domain’s id to link to the show page, and depend on the toString() implementation on the domain class in order to render the the text that will displayed for each list item.

Footer

<div data-role="footer" data-position="fixed">
  <g:set var="offInt" value="\${params.offset == null?0:params.offset.toInteger()}" />
  <g:set var="offNext" value="\${offInt + params.max}" />
  <g:set var="offPrev" value="\${((offInt - params.max)<0)?0:(offInt - params.max)}" />
  <div data-role="controlgroup" data-type="horizontal">
    <g:link action="create" data-icon="plus" data-theme="b">
      <g:message code="default.button.add.label" default="Add" />
    </g:link>
    <g:if test="\${offInt > 0}">
      <g:link action="list" params='[offset:"\${offPrev}"]' data-icon="arrow-l">
        <g:message code="default.nav.previous.label" default="Previous" />
      </g:link>
    </g:if>
    <g:if test="\${(${propertyName}List.size() < ${propertyName}Total) && 
                (offNext < ${propertyName}Total)}">
      <g:link action="list" params='[offset:"\${offNext}"]' 
              data-icon="arrow-r" data-iconpos='right' class="ui-btn-right">
              <g:message code="default.nav.next.label" default="Next" />
      </g:link>
    </g:if>
  </div>
</div>

Similar to the header, I’ve set the footer to follow the page scroll and always be displayed at the bottom by defining the data-position=”fixed” attribute. Within the footer you will find my humble attempt at a simplistic paging code which is probably buggy, but it worked fine with my tests, and is uncluttered enough for the purpose of showing in this article. Another thing which is cool is the data-role=”controlgroup” div, which will group buttons/links inside it into a grouped set of buttons which look really cool. You will also notice that I’ve added icons (which are part of the default themes) using the data-icon attribute, and changed the theme using the data-theme attribute.

Creating the application

Following similar steps I edited (mobil-ized) the show, create and edit templates so that I can scaffold all the views for my test application.

Next a created two simple domain classes, an Account domain with a one to many relationship to a Transaction domain something like this.

class Account {

  String name
  String description
  List transactions = new ArrayList()
  static hasMany = [transactions:Transaction]

  static mapping = {
    transactions(sort:"txnDate", order:"desc",cascade:"all-delete-orphan")
  }

  String toString(){
    "${name}"
  }
}


class Transaction {

  double amount
  String category
  String comment
  Date txnDate
  Account account

  static belongsTo = [account:Account]

  String toString(){
    "${comment} (\$${amount})"
  }
}

Then all I had to do is grails generate-all for both domain classes, and I was left with a fully functional mobile web app that allows me to manage my expenses while on the go. Below are some images of how the app looks when run on an android emulator (you can also just fire it up in your browser).

Expenses App Home Page

Expenses Home Page

Expenses Home Page

Accounts List Page

Accounts List

Accounts List

Show Account Page

Show Account

Show Account

Add Transaction Page

Create Transaction

Create Transaction

In my examples, I have only touched the surface of what the framework has to offer though, by looking at the docs, you can take advantage of the new events that are available to create really cool stuff that are applicable for touch enabled devices, for example one cool thing that could be done is adding a tapHold event to the list items in the list page, which will popout a dialog asking whether you want to edit, delete or view this domain instance, similar to how native mobile applications do it.

Keep in mind that this is still an alpha release and it might have some missing features (e.g. some missing components like the data picker) and might still have some bugs, but I will be keeping an eye for jQuery mobile and will most likely use it once it is stable on a project that I am currently working on.

If you are interested, here is the full source for my sample application.

Posted in Programming.

Tagged with , , , .


0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.



Some HTML is OK

or, reply to this post via trackback.