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>
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:
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:
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:
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:
Follow @bob_buzzard
<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!
Follow @bob_buzzard
I tried it with Opera Mini Browser on Android (S2) and it looks real good.
ReplyDeleteThanks Raven!
ReplyDeleteBob, It worked like charm on my android device.
ReplyDeleteA quick question, i have a custom apex/visualforce solution that i am planning to make it mobile ready. should changing the regular jQuery library to jQuery mobile does the trick?
Your page was like wizard , so how does this behave for single interface pages which have buttons and pop-ups?
What are your thoughts?
Thanks,
Harry Jobs
Hi Harry,
DeleteEach of the pages in the survey can act as a standalone. This isn't a true JQueryMobile "app" where all pages are present as divs in a single HTML page. Thus it should behave appropriately.
In terms of making it mobile ready, you'll need to change the attributes on the HTML elements in the main, and change the page structure to map that of JQM. I don't think you'll just be able to flip it that easily - I've built mine from scratch from the page side, but been able to reuse controllers across JQM and regular pages.
Thanks, Bob for your insight.
ReplyDeleteBest,
Harry
Hello,
ReplyDeleteyour blog has very useful content for developer prospective.
thanks for the help man.
You can do it with some simple CSS tricks or jQuery plugins. Try the effect as a background, or as a header or footer. Don't go overboard and frustrate your users.
ReplyDeletejquery jobs
Is there any way to refer to the new account page after saving the record without going to the standard salesforce page?.
ReplyDeleteregards
rk
Definitely. The simplest way would be to create an extension controller that implements its own save method. This delegates the actual writing to the database to the standard controller, and can then return a reference to whatever page you like.
DeleteHi Bob, thanks for the awesome work. Did you try swipe function from jquerymobile on VisualForce page? I tried to implement that, but no luck. If you know how, could you write a post for that?
ReplyDeleteThanks
Hello Bob, how is onclick="save();" is associated with the apex:actionfunction Save? I am not able to get the code running.
ReplyDeleteWhen you define an actionfunction, this creates a JavaScript function based on the name. So as I have an actionfunction with the name attribute of 'save", Visualforce creates a JavaScript function called 'save' that executes the postback. Thus I can execute the save() method from the onclick handler and that will post the form back. Check out the docs for actionfunction at : http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_actionFunction.htm
Deletegood post thank you
ReplyDeleteanyone can easily learn
great post with proper example
ReplyDeletethanks dear
Hi Bob
ReplyDeleteI just copied and tried the code here in my dev org. Somehow the scripts do not load and give me a plain looking vf page page.
Could you please advise?
Regards
Rajat
Great post and insights. I really want to thank you for taking the time to put this together.
ReplyDeleteNowadays, with a variety of uses sky is the limit for mobile applications. An amazing mobile app regularly makes a similarly great online vicinity in the applications market.