Adding Mobile Views to your Grails Applications with JQuery Mobile: A Real Life Example

Last month, we launched mobile views for Secret Escapes, our little application written in Grails.

For this project, we chose to implement a set of mobile views on top of our existing Grails application using jQuery Mobile. In less than a month, we were able to implement and put live a set of views that simplified the user experience for our mobile users.

In this post, I will discuss some of the tools, challenges and design decisions that arose from making our existing Grails application more iPhone friendly.

The End Result

The screenshots above demonstrate how Secret Escapes looks like when viewed from an iPhone or Android phone.

Tools Used:

Choosing the Right Architecture.

Before starting this project, we considered a few alternatives for building out these mobile views:

  1. Use principles of responsive design to change our existing site into one that would adapt to the user’s device. This would involve having only one set of views and having our design adjust based on the user’s device width. ( See some examples here, and read this book for more details ).
  2. Build a set of alternative views using the same grails controllers. This would involve creating a set of views for mobile and one for the desktop version of our site.
  3. Expose our existing functionality as a set of APIs and built views around the exposed services. This would allow us to outsource the design of the mobile views to design agencies and external companies with more experience, as well as facilitate the creation of iPhone and mobile apps using the same code base.

While we loved the idea of responsive design, we felt that it would involve substantial changes to our existing website. Given that our site has already been running for a year, we felt that it would introduce too much unnecessary change and force us to validate changes on both mobile and desktop versions of our site across different browsers and devices.

Similarly, we liked the idea of using a set of APIs. However, we felt that the process would be very time-consuming and it would be difficult to get things like proper authentication done correctly.

At the end, we chose to generate a set of alternative views based on the device’s user agent. We felt this solution would allow us to A/B test individual views and really optimize interactions for the smaller devices. Our site has a fairly complicated booking selection engine, and we felt that trying to jam that into one long column would be very difficult to do right.

Choosing an UI Framework

After deciding that we were going to build alternative views, we quickly evaluated three frameworks:

  • jQTouch – a jQuery plugin optimized for Webkit
  • jQuery Mobile – a jQuery framework for mobile
  • Sencha Touch – a slightly more robust framework base on Ext JS and jQTouch.

We decided to go with jQuery Mobile since it seemed to be more actively maintained and focused on more devices than jQTouch.

Compared to Sencha, we felt the HTML-focused approach of jQuery Mobile ( you just decorate your divs ala Twitter Bootstrap ) would be easier to understand and update compared to Sencha’s more Javascript focused approach.

Adding Mobile Views to Your App

So how does it all work?

We server both mobile and browser views from the same Grails application. Requests all go through the same set of controllers and actions.

When an user comes to our site, we sniff the user agent of their browser and either present our traditional views or the new mobile view. It’s pretty straightforward.

The following sections will describe the steps in this process in a little more details:

Add a Device Detection Service.

We used the Spring Mobile plugin for grails to decide if we wanted to show mobile views.

Spring Mobile comes packaged with the WURFL mobile device detection repository for resolving mobile devices. In theory, WURFL detects mobile devices and provides the server with a list of capabilities for this mobile device. In practice, this became a bit of overkill and we just resorted to using the user agent for mobile detection.

The Spring Mobile plugin injects a dynamic method called withMobileDevice into Grails controllers to help deal with mobile devices. We found that while this was nice for one or two views, it made it very difficult to centralize our device detection.

Instead, we added a Mobile Service that will detect mobile devices uniformly across our entire application.  The Spring Mobile plugin injects an attribute in your request called CurrentDevice containing device information. We use this in our service to determine whether to show mobile views or not. We also use the user agent in the request to bypass iPads and Android tablets.

The detection code in our service looks like this:


boolean detect(request) {

   def device = request.getAttribute('currentDevice')

   boolean detected = device.isMobile()

   if (detected) {

     def userAgent = request.getHeader('User-Agent')

     if (userAgent?.contains('iPad')) { // skip iPads

        detected = false

     } else if (userAgent?.contains('Android') && !userAgent?.contains('Mobile')) {

        // and android tables

        detected = false

     }

   }

   detected

}

Change to Grails Controllers to Show Alternatives.

Once we have our device detection code in place, we needed to alter our controllers so they would render the mobile views for certain actions.

Adding interceptors to Controllers.

In the ideal world, we would just add a filter that modified the modelAndView after a request has been processed. In Grails 1.3.7, however, we can’t really tell the current modelAndView in a filter due to this bug [GRAILS-6555].

Therefore, we have to add an after interceptor to our controllers. In the simplest case, it is just a matter of appending a /mobile/ prefix to all the views rendered. We then create a set of different views under the mobile directory under views.


def afterInterceptor = [action: this.&handleMobile]

private handleMobile(model, modelAndView) {

   if (modelAndView && mobileService.detect(request)) {

      modelAndView.viewName = '/mobile/' + modelAndView.viewName

   }

}

This gets a little tricky when dealing with AJAX calls, however, as we don’t want alternative views rendered when we’re returning html fragments.

In some controllers, we need to limit the actions where alternative views are rendered:

def afterInterceptor = [action: this.&handleMobile, only: ['currentSales', 'offers']]

In some other places, we want to render a full view and do a full page load instead of just returning an Ajax fragment. We do this by invoking the mobile service directly in the controller:


if (mobileService.detect(request, session.affiliate)) {

   render(view: '/mobile/sale/book', model: model )

} else {

   render(template: '/sale/bookingDetails', model: model)

}

Once these changes have been made to the right controllers, we can effectively display different views using the same logic within the controllers.

Converting existing Grails views into Mobile.

OK, so at this point, we have convinced our Grails controllers to render a set of alternative views. What kind of witchcraft do we have to do to actually work with JQuery Mobile.

Luckily for us, the jQuery Mobile docs are really good in explaining how to set up documents that work with it.

One view to one mobile page

The simplest conversion we had to do in our site was to match one view with one page in JQuery mobile.

So essentially, we’re converting a full view into one that fits into the mobile world and uses jQuery Mobile widgets instead of HTML elements.

The basic jQuery Mobile page essentially is just a Page Div with a Header and Content Div file. See ( Single Page Template ) in JQuery Mobile docs.

This can be simplified into a Grails layout shared with all other views with the help of content blocks for different sections.

One view to multiple mobile page

The more complex scenario for our application is where we break down the functionality of one existing grails view across multiple jQuery Mobile pages.

This option allows us to break down information returned from one controller in Grails into multiple pages. Compared to a traditional responsive design approach, this gives us a little bit more flexibility in organising content and interactions in a way that is more mobile-friendly.

In the picture below, our sales page, you can see that this design is not very mobile friendly as it contains many different sections describing different things. When breaking this into mobile views, we simply take the parts of the pages we want and put them into different mobile pages, highlighted in red.

JQuery mobile provides the ability to add multiple pages and link them externally via anchor tags. See ( Multi-Page Templates ) in documentation.

Given that we only had one page that required this, we coded the page without using the help of Layouts and Tag Libraries, but a case can definitively be made that having a good and simple way to define pages would be very helpful in a JQuery Mobile plugin.

Re-using existing GSPs

For some of our more complex functionality such as our booking forms and calendar selectors, we reuse the same gsp template in both desktop and mobile versions.

To make the process easier, we pass in an ‘mobile’ parameter to our mobile views and alter their content hiding or adding new elements needed for the new UI.

JQuery Mobile and AJAX

One of the features of JQuery Mobile is that it tries to load pages via AJAX by default. We found that this got in the way of our site, as it broke existing AJAX interactions.

We solved this problem in two ways.

For the pages with extensive number of AJAX called we simply disabled jQuery Mobile’s AJAX commands via the ajaxEnabled configuration parameter.

For other areas such as login and user sign up, we changed the request at the controller level so that it no longer an AJAX call.

Testing Mobile Views with Geb.

One final step that we needed to do to ensure that changes to our mobile application were being tested automatically in our functional tests was to modify Geb to accommodate both mobile and non-mobile views. Our solution is outlined in this blog post.

Eye Candy and Other Stuff

JQuery Mobile comes with a really nice Theme Roller. This allows you to create really nice buttons and backgrounds using proper gradients.

In development, it was very useful to use firefox and Chrome extensions that modified the user agent. This allowed us to test both our device detection filters and not have to switch to devices until the final testing phase.

There is a mobile bookmarking bubble by google that reminds your users to add your site to their home screen.

Instead of looking for a JQuery-Mobile specific plugin, look for a JQuery plugin that is touch enabled. The subtle difference here is that you can re-use the components across your existing site, and you have a wider selection.

JQuery Mobile is a proper widget toolkit. It’s quite refreshing how quickly you can build a usable interface by just annotating your HTML.

Final Thoughts.

Overall, I was really surprised at how quickly it took to add mobile views to our Grails application. In less than a month, we were able to add a comparable experience to our mobile users than on our actual site.

It is still very hard to squeeze good usability into a small screen. A typical pattern in Grails applications is to highlight fields that don’t pass validation in red. We found, however, that in mobile devices this ended up being fairly counter-intuitive, as the red boxes and error messages would render off-screen for the user.

In our app right now, we only separate them into mobile and non-mobile views. But there is nothing in this approach that won’t allow it to be extended so that we would have mobile, tablet and desktop views.

We never really started approaching this project because it seemed fairly daunting. Given that you don’t have to work with ancient browsers, the process is actually fairly quick and painless. I highly recommend anyone thinking about creating mobile views to get started now.

Advertisement

Like this:

Be the first to like this post.