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 , , , .


20 Responses

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

  1. website traffic says

    What’s up, just wanted to say, I loved this post.
    It was helpful. Keep on posting!

  2. seo tutorial says

    Simple search in Google for your keyword phrase in quotes.
    First of all, place the important keyword or keyword phrase in your title,
    and then a few times in the body of the post.
    These types of search terms are highly desirable because they are what your potential
    customers type in when they’re ready to buy what you offer.

  3. Mikayla says

    Ahaa, its fastidious dialogue about this post here at this blog, I have read all that, so at this time me also commenting at this place.

  4. depression says

    I աas able to find good information from your content.

  5. Laurinda says

    We stumbled over here different website and thought I should check things out. I like what I see so now i’m following you. Look forward to looking into your web page again.

  6. Linette says

    Fantastic site. Lots of helpful info here. I’m sending it to several friends ans additionally sharing in delicious. And certainly, thank you in your sweat!

  7. Monique says

    always i used to read smaller articles that as well clear their motive, and that is also happening with this article which I am reading at this time.

  8. Shayna says

    In fact no matter if someone doesn’t be aware of then its up to other people that they will help, so here it takes place.

  9. dot com realtor says

    Good day! I could have sworn I’ve visited this web site before but after looking at a few of the posts I realized it’s
    new to me. Regardless, I’m certainly happy I came across it and I’ll be book-marking it
    and checking back regularly!

  10. jackery giant high-capacity premium aluminum says

    Yoou may be able to stick it to the man wwho runs the power company for dayus at a time.
    You won’t be able to do anything with it until you have fixed
    the reversed cell. It is easier tto prevent this inconvenience with better planning by looking at
    the benefots oof using a portable airr conditioner.

  11. Abby says

    I rarely comment, but I browsed a few of the responses on this page Creating mobile web applications with jQuery mobile and Grails. I actually do have a few questions for you if it’s allright. Is it simply me or do a few of the responses look like they are written by brain dead people? :-P And, if you are writing at other social sites, I’d like to follow you. Could you list of every one of all your social community sites like your linkedin profile, Facebook page or twitter feed?

  12. , says

    Hello! I know this is kinda offf topic however , I’d fivured I’d ask.

    Would you be interested inn trading links or maybe guest writing a blog post
    or vice-versa? My site discusses a lot of tthe samme topics
    as yours and I believe we could greatly benefit from each other.

    If you’re interested feel free tto shoot me an e-mail.

    I look forward to hearing from you! Superb blog by the way!

  13. iphone application developer says

    Thanks for another magnificent post. Where else may anyone get that type of
    information in such an ideal means of writing?
    I have a presentation subsequent week, and I am at the look for such info.

  14. Timmy says

    I’ll right away take hold of your rsss feed as I can not to find your
    email subscription hyperlink or e-newsletter service. Do you’ve any?

    Please allow me understand in order that I may subscribe.
    Thanks.

  15. hot water heater says

    I just could not depart your web site before suggesting that I extremely loved the usual info an individual provide
    to your guests? Is gonna be again continuously to check out new posts

  16. Shawn says

    I pay a visit day-to-day some blogs and information sites to read articles, except this web site gives feature based content.

  17. Lyle says

    Howdy just wanted to give you a brief heads up and let you know a few of the images aren’t loading properly. I’m not sure why but I think its a linking issue. I’ve tried it in two different browsers and both show the same outcome.

  18. affilliate marketing says

    Howdy! This is my 1st comment here so I just wanted to give a quick shout out and tell you I truly
    enjoy reading your posts. Can you recommend any
    other blogs/websites/forums that cover the same subjects?
    Thank you!

  19. best hookah bars in queens says

    Hello to every one, the contents existing at this website are actually awesome for people knowledge,
    well, keep up the nice work fellows.

Continuing the Discussion

  1. Womens Deck Shoes Sale linked to this post on July 22, 2014

    Womens Deck Shoes Sale

    Creating mobile web applications with jQuery mobile and Grails – train of thought



Some HTML is OK

or, reply to this post via trackback.