Sharing Our Passion for Technology
& Continuous Learning
Mobile Grails
Background
I was recently asked to throw together an application that would register and score a model car race. I went home and created a Grails app. Unfortunately this race will be held in a building that doesn't have internet access. So I tried to keep my pages compact and clean so they could be used on mobile phones.Research
After I created the first release it kept bothering me that my app was not designed for mobile devices. So I visited the Grails Plugins page and searched for the word "mobile". The following results caught my attention:I threw out iWebKit immediately because it only targets iPhone, iPod Touch and iPad. While iUI "now supports most smartphones & tablets" it's current version is 0.40alpha1. Furthermore the Grails plugin doesn't appear to have been updated in the past 2 years.
This left me with Spring Mobile Grails and jQuery Mobile. While I naturally gravitate to Spring products, jQuery Mobile Scaffolding had me at the word "scaffolding". Besides, their page states that they support the following devices.
I'm a visual person and the sight of all of these phones convinced me to try this plugin.
jQuery Mobile Scaffolding Plugin Tutorial
The rest of this post details my experience cutting my site over to use the jQuery Mobile Scaffolding plugin.
Setup First I installed the plugin
grails install-plugin jquery-mobile-scaffolding
| Loading Grails 2.0.0
| Configuring classpath.
| Environment set to development.....
| Resolving plugin jquery-mobile-scaffolding. Please wait...
| Installing zip jquery-mobile-scaffolding-0.1.zip.....
| Installed plugin jquery-mobile-scaffolding-0.1
| Resolving plugin JAR dependencies.....
| Plugin installed.
grails install-mobile-templates --non-interactive
| Loading Grails 2.0.0
| Configuring classpath.
| Environment set to development.....
| jQuery-mobile templates installed successfully
I renamed my custom business logic and views so the scaffolding would not overwrite my code.
Then I ran "generate-all" for all of my domain classes
grails generate-all [my domain class package & name]
Then I merged my controller and view changes back into the newly generated code.
Then I started up my app.
grails run-app
I opened the site with my iPhone and I was really impressed with the clean look and navigation.
Before I continue, I must admit that I was thrilled to learn how to create an iPhone screenshot. What a great feature.
iPhone Issues
After playing around with this app I found the following issues.
1. The list pagination links look strange and are difficult to use.
2. The flash messages are obscured by the search box on the list screen.
3. The menu floats over the form that I'm trying to complete.
Google Chrome Issue
4. Number Validation
In order to get my iPhone to launch the number keyboard instead of the standard letter keyboard I used the HTML5 input type.<input type="number" value="" />
As I was testing this on my iPhone and various desktop browsers I ran across the following error.
Apparently Google Chrome provides client side validation. Unfortunately I want users to be able to enter decimal values.
Fixing The Issues
Instead of fixing each page separately I modified the scaffolding templates located in "src\templates\scaffolding\". ``` -src --template ---scaffolding ----Controller.groovy ----create.gsp ----edit.gsp ----list.gsp ----renderEditor.template ----show.gsp ```1. To fix the pagination issue, I replaced it with an iPhone style "Show ... More" button. This required changes in the action and the list.gsp.
Changes for 'src\template\scaffolding\list.gsp'
...
</g:each>
</ol>
<g:if test="\${showMoreSize > 0}">
<g:link data-role="button" action="list" params="[max:max]">Show \${showMoreSize} More</g:link>
</g:if>
</div>
...
Changes for 'src\template\scaffolding\Controller.groovy'
...
private static final int RESULT_SIZE = 10
...
def list = {
def entityListSize = ${className}.count()
def max = params.max ? params.int('max') : 0
max += RESULT_SIZE
max = Math.min(entityListSize, max)
def showMoreSize = entityListSize - max
showMoreSize = Math.min(showMoreSize, RESULT_SIZE)
params.max = max
[${propertyName}List: ${className}.list(params), max: max, showMoreSize: showMoreSize]
}
...
2. To fix the message issue, surround the message with paragraph tags.
Changes for 'src\template\scaffolding': 'create.gsp', 'edit.gsp', 'list.gsp' and 'show.gsp'
<g:if test="\${flash.message}">
<div class="message"><p>\${flash.message}</p></div>
</g:if>
3. To fix the floating menu issue modify the header with the following:
Changes for 'src\template\scaffolding': 'create.gsp', 'edit.gsp', 'list.gsp' and 'show.gsp'
...
<body>
<div data-role="header" data-position="inline">
...
4. To change the whole number validation to accept decimal values make the following change:
This should be changed on a case-by-case basis.<input type="number" step="any" value="" />
The 'step' attribute "specifies the value granularity of the element’s value." In other words, the default behavior of a number input only accepts whole numbers. The 'step' attribute is used to allow decimal values.