Apr 2, 2015

Salesforce1 Mobile - Fun with IFrames & Matching the Styling

When you start developing for mobile, you tend to find reasons for harnessing the device features that they come with (camera, location services).  If you've ever had the need to snap a photo from Salesforce1 and attach it to a record(in a custom VisualForce page of course), you may have come across the wonderful tag <apex:InputFile />.  It's a definite time-saver versus having to handle the complexities of file formats, sizes, viewstate, governor limits etc.

My guess is that you've also ran into issues when trying to rerender a part of this visualforce page, or perform an "oncomplete" action.  You would get this error if so:

apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute

This means that any other part of the page that attempts one of these actions will not be able to do so, simply because the page contains an InputFile tag elsewhere.  I quickly found this solution (using an ActionRegion tag to wrap other parts of the page) that worked initially but as my page grew, the InputFile component broke. The component stopped firing the setter for my Attachment property.  The more I worked with trial and error, I decided to cut my losses and encapsulate it within an iframe so that I could code with freedom on the main page.

It figures that I would find other solutions AFTER taking my plunge into this solution, but it was fun, and I can use this in other ways :)!  Here are other options to using Apex:InputFile tags in more complex situations: https://developer.salesforce.com/forums/ForumsMain?id=906F0000000Acd6IAC

A solution to divide the pages:

  1. Attachment page: Create a page that takes an ID as a url parameter for the ParentId, to be used to insert the attachment. In this page you have the InputFile tag which handles the file. 
  2. Parent page: Show the attachment page in an IFrame. Use javascript functions that can be fired between the child and the parent windows for a seamless user experience however you see fit. (same domain).

Link: Demo

  1. This works in all browsers.
  2. The demo site was built for Salesforce1 and is a great example of using Bootstrap SF1 Theme along with the standard Bootstrap Javsascript library (matching the version to 3.0.1).
  3. The demo is responsive and works in the Salesforce1 app on mobile devices, desktop, and Salesforce1 through the browser.
  4. Other improvements would be
    1. Implement swipe for the Details and Attachments tabs, and convert to "tap" instead of "onclick" events. 
    2. Best Practice: Use JS remoting and remove the view state for better mobile performance.  Implementing remoting on the parent page could be done with this iframe solution being handy.

Link: GitHub

Keys to the solution:

AccountDetail page
  • Contains an IFrame with id = "attachmentIFrame"
  • Contains the submit button as part of the modal. It fires a javascript function from within the iframe to insert the attachment.
  • Contains a close button which is unlimited and able to call an actionFunction which does contain an oncomplete rule (which navigates the user to the attachments tab list).

AttachmentUpload page
  • Contains the Apex:InputFile tag, and has an accessible javascript/actionFunction called "processAttachment". This is the function that the parent page will call.

AttachmentUpload class
  • Contains page accessible method "ProcessAttachment" which will take the attachment property and perform the DML insert.

Salesforce1 & other Goodies:

  • Salesforce1 navigation vs desktop navigation are different, see this JS function than helps to handle this (used here when clicking on a link to the attachment).

  • When the user clicks the Close button after they uploaded the attachment(s), we are able to show the Bootstrap tab very easily.

By separating the inputFile tag, you can use rerender or oncomplete attributes on the parentpage again, without the need for actionRegion tags or multiple Form tags.

Styling is done with the BootstrapSF1 package and the Bootstrap Javascript library to match. This will work in Salesforce1.