I've been spending some of my time building mobile functionality recently.
One thing that has put me off in the past has been the multitude of
devices that need to be supported if I want to maximize usage. Building
the same functionality for iOS, Android and others doesn't fill me with joy.
HTML5 has the "write once, run anywhere" capability that first attracted me to
Java back in JDK 1.0 days. The downside to this it that it does reduce the
functionality available. For example, offline storage is problematic at
the moment with different browsers supporting different standards, and the
standards themselves being subject to change, while access to native
functionality is still in very early days (Android makes the camera available
through javascript but there aren't many other examples out there). It
very much feels like the future though, so I've been heading down that route.
My first foray into developing a mobile front end was for our BGFM product for
Dreamforce 2010. This was simply some Visualforce pages sized and styled
appropriately for the iPhone. The obvious downside to that was that a
device with any other form factor needed separate pages (or at least pages
that could adjust themselves appropriately). However, towards the middle
of 2011 I came across JQuery Mobile (JQM) and starting using the 1.0 alpha
releases. The great thing about JQM is that it takes care of the cross
device side of things, leaving you with one app that works across all popular
smartphones, tablets etc. Even better, it will also work against older
devices, dropping back down to basic HTML if necessary. For details,
documentation, tutorials and samples, check out the
JQuery Mobile site.
Building Visualforce pages that leverage JQM is pretty straightforward.
Lifting from the getting started page from the JQM site:
<apex:page showHeader="false" sidebar="false" standardStyleSheets="false">
<html>
<head>
<title>My Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
<body>
<div data-role="page">
<div data-role="header">
<h1>My Title</h1>
</div><!-- /header -->
<div data-role="content">
<ul data-role="listview" data-inset="true" data-filter="true">
<li><a href="#">Acura</a></li>
<li><a href="#">Audi</a></li>
<li><a href="#">BMW</a></li>
<li><a href="#">Cadillac</a></li>
<li><a href="#">Ferrari</a></li>
</ul>
</div><!-- /content -->
</div><!-- /page -->
</body>
</html>
</apex:page>
Opening this in a mobile device gives:
As you can see from the code, I've had to do nothing to style this
appropriately for the device, simply giving the unordered list a data-role of
listview means that JQM takes care of all the heavy lifting. Adding buttons
for simple navigation is also straightforward, and JQM provides some
transitions to mimic native apps. Again, its all taken care of by the markup:
<a href="index.html" data-role="button" data-inline="true">Cancel</a>
<a href="index.html" data-role="button" data-inline="true" data-theme="b">Save</a>
resulting in a couple of appropriately styled buttons at the bottom of the
page:
Where things get a little more interesting is if you have a form in the page
and the buttons are submitting the form rather than simply moving to a new
page, as is the case in most of the Visualforce pages I write.
In that case I'd like to use an <apex:commandLink> component, but I
can't provide the additional attributes to lay things out correctly - e.g.
data-inline="true". While I could write some Javascript to take care of this,
in the first instance I'm trying to keep things simple, so I use an
<apex:actionFunction> component and tie this to the JQM specific link
via an onlick handler as follows:
<apex:form id="jsfrm">
<apex:actionFunction action="{!save}" name="save"/>
.....
<a href="#" data-role="button" data-inline="true"
onclick="$.mobile.showPageLoadingMsg(); save()">Save</a>
.....
</apex:form>
The $.mobile.showPageLoadingMsg(); function call simply displays a
spinner to let the user know something is happening.
Standard <apex:inputField/> components also render well most of
the time - sometimes I end up using the input text/textarea/checkbox etc to
allow for a greater level of control, but a basic form can be created with the
minimum of effort. For example, a simple form to create an account by
specifying its name and industry only requires the following markup:
<apex:page showHeader="false" sidebar="false" standardStyleSheets="false" standardController="Account">
<html>
<head>
<title>Create Account</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
<body>
<div data-role="page">
<div data-role="header">
<h1>Create Account</h1>
</div><!-- /header -->
<div data-role="content">
<apex:form id="jsfrm">
<apex:actionFunction action="{!save}" name="save"/>
<apex:outputLabel for="name" value="Name"/>
<apex:inputField id="name" value="{!Account.Name}"/>
<apex:outputLabel for="industry" value="Industry"/>
<apex:inputField id="industry" value="{!Account.Industry}"/>
<a href="index.html" data-role="button" data-inline="true">Cancel</a>
<a href="index.html" data-role="button" data-inline="true" data-theme="b"
onclick="$.mobile.showPageLoadingMsg(); save();">Save</a><br />
</apex:form>
</div><!-- /content -->
</div><!-- /page -->
</body>
</html>
</apex:page>
Produces the page shown below - not too bad for a few lines of markup:
Obviously once the record is saved, the user is redirected to the standard
view page which looks pretty awful on a phone, so standard controllers
probably aren't going to be a silver bullet for this, making it the usual
challenge for editions below enterprise.
I've put together a demo application that allows a contact to take a survey.
Its hosted on an unauthenticated Force.com site so is publicly available.
A couple of sample screen shots are shown below:
If you'd like to try out the demo application, simply
click here.
A couple of points to note:
-
I've put this together over a few weekends so I wouldn't be in the least
bit surprised if there were some glitches waiting.
-
I've only tested it with an iPhone and webkit browser, though I can't see
why there would be issues with different devices.
-
This is a real functioning application, so the feedback will be stored in
my Salesforce instance - so if you feel like leaving real feedback, I may
even act on it!