Salesforce1 & Visualforce 101

Jay Desai
Salesforce Sage
7 min readDec 30, 2015

--

When I started learning how to customize Salesforce1, I was overwhelmed with all the articles out there. Each one taught me a concept, but it was a challenge to piece everything together into something that you could use in practical situations. There was always some detail that was missing in the tutorial that you had to hunt down yourself or learn by trial and error. This blog aims to bridge that gap by walking through common use cases end to end.

Before we dive into the use cases, lets start with some basics:

Look and Feel:

Salesforce has made public the stylesheets they use to style pages in Salesforce1 and the new Lightning User Experience. This is known as the Lightning Design System. This is the easiest way to create great looking and responsive pages that make your apps seamless with the rest of the Salesforce1 experience.

How to set this up for use in our examples:

  1. Download the zip file that contains the CSS framework under the heading “Design System Zip”.
  2. Upload the zip file as a static resource.

We will get into this deeper later, but couple of important things to note about the framework:

  • You need a encapsulate any markup that utilizes the CSS with a parent div tag:
<div class="slds"> all my other tags </div>
  • You could take out parts of the CSS and directly embed them in your page, but this is not recommended for production use.
  • For every major styling pattern there are examples you can directly copy from the Lightning Design System site under “Components” on the left-hand menu. These examples don’t surround their markup with the parent div tag mentioned above, so remember to do that!

Programming Patterns

There are several patterns you can utilize in creating a custom Salesforce1 page. In this tutorial, we won’t go deeply into each one since there is a great Trailhead module that walks you through the basics. In the use cases in this article, we’ll be using a combination of RemoteObjects and Javascript Remoting.

What you ultimately decide to use in your designs will vary, but all other things being equal, a good design balances between minimizing page size, having a mobile optimized UX, and implementing use cases optimized for mobile situations (i.e micro-moments) as opposed to things you would do on the desktop.

Standard Setup of the Apex Page

If you want to have complete control of how the page looks, which we will do in our examples, there are some typical things you’ll do to setup the page.

Setup the <Apex:Page> tag as shown below to have complete control over styling. Mentioning the “docType” is especially important as it also allows you to access device specific features such as specific keyboard layouts depending on input field type. Although a controller is not always needed depending on your use cases (I.e. when you exclusively use RemoteObjects), you can also add controller or extensions tags for the page to reference.

<apex:page showHeader="false" standardStylesheets="false" sidebar="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0">

Setup the Stylesheet for the page to reference as shown below. In the example, the static resource name is “slds0121”. Change this to the label you used when uploading the CSS zip file.

<head>
<apex:stylesheet value="{!URLFOR($Resource.slds0121, 'assets/styles/salesforce-lightning-design-system-vf.css')}" />
</head>

Setup the <HTML> tag as shown below. This is needed for the CSS framework.

<html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

Let’s put all this together to implement a specific use case!

Use Case 1: Page that shows a list of records and allows you to edit a field in each row.

  • The page will be an Action on the Opportunity record.
  • It will use RemoteObjects and Javascript Remoting to call a Controller method.
  • Need a custom object called OpportunityContact with the following custom fields:

How the page will look:

The full code is located here:

ContactListAction : The page you will refer to when you are creating the Visualforce action on the Opportunity Object.

OpportunityContactList: The page that displays the Contact list.

ContactListController: The class that contains the remote method called by the OpportunityContactList page.

Walk-through of key steps in creating the page:

Define the RemoteObject

<apex:remoteObjects >     <apex:remoteObjectModel name="OpportunityContact__c" fields="Id,ContactName__c,Contact__c,Account_Name__c,Commission_Split__c"/>

</apex:remoteObjects>
  • You need to specify every field you will reference in any code on the page explicitly.
  • You can also use aliases to shorten names by using jsShorthand.

Structure the Body

You can use examples from the Lightning Design System site to get specific HTML mark-up structure to achieve the desired visual effects. In this case, we’re using the “Data Tables” examples. Leave placeholders for dynamically generated HTML. In this case the body of the table (everything in-between the <tbody> tag).

<table class="slds-table slds-table--bordered">
<thead>
<tr class="slds-text-heading--label">
<th scope="col">
<span class="slds-truncate">Name</span>
</th>
<th scope="col">
<span class="slds-truncate">Comission %</span>
</th>
</tr>
</thead>

<!-- Placeholder for HTML that will be created by JS -->
<tbody id="tblContent">

</tbody>
</table>

Create your “actions” on the page via buttons

The button will call the Javascript Update function which will make a remote call to the Update method in the Controller Class.

<button class="slds-button slds-button--neutral" onclick="Update()">Update Commission %</button>

Create the Javascript

In our situation, we will place the Javascript towards the end of the page to make sure it executes after the HTML elements are loaded. There are varying ways to handle this, but we’ll use the simplest way in our example.

  1. Retrieve records to display on the list

The {!$CurrentPage.parameters.uid} will be defined when we create the corresponding Action. This will pass in the Opportunity Id so we only display a list that contains records from the Opportunity record the action was called from.

A key part of this is iterating through the records and populating the placeholder html tag we defined earlier with elements that represent each data row. For each record, we will create an input tag with an id attribute value that corresponds to the record ID and a value that corresponds to the Commission Split field. We will reference these attributes later when we call the remote method to save changes in the database.

var ocr = new SObjectModel.OpportunityContact__c();ocr.retrieve({where: {OpportunityContacts__c: {eq: '{!$CurrentPage.parameters.uid}'}}}, 

function(error, records) {
if (error) {
alert(error.message);
} else {
var html = "";
records.forEach(function(record) {
html = html + "<tr class=\"slds-hint-parent\"><td class=\"slds-row-select\">" +
record.get("ContactName__c") + "</td><td><input id=" + record.get("Id") + " type=\"number\" style=\"width: 50px;\" value=" + record.get("Commission_Split__c") + "></td></tr>";
});
tblContent.innerHTML = html;
}
}
);

2. The Javascript Remote function to save changes to the database

Once the button on the page is clicked, it will call the Update() function in JavaScript.

Iterate through the input boxes to get the values and the corresponding record IDs. We will store them in a multi-dimensional array which we will pass to the remote method. There are variety of ways to collect values from multiple fields in Javascript, we are using one possible way.

//Create an array with a reference to all input tags on the page.
var inputs = document.getElementsByTagName("input");
/* Create an array to store the information we need from the input tags */
var myarray = [];
/* Iterate through the input tags and populate the array we will pass to the remote method. The way the push method is passed parameters creates a 2 dimensional array. Recall that the input tag id attribute is set to the record id for that particular row on the list.*/
for (var i=0; i < inputs.length; i +=1){
myarray.push([inputs[i].id, inputs[i].value]);

}

Note: There are several ways to assign values to an array. For Visualforce to do the necessary type conversions, you need to use the “push” method to populate the array.

Next call the remote method passing the array. I will not get into the details of this as it is fairly well documented here.

Create the Controller Class

Creating the method to handle the database save is pretty straightforward with a couple of caveats:

  • You need to use the @RemoteAction token before the method name.
  • You need to define the method as global static. The implication of this is that the method will have no access to viewstate/context, so you need to make sure you pass in all the necessary parameters to execute what it is trying to accomplish.
global class contactListController {

public list<list<String>>updateValues{get;set;}

@RemoteAction
global static String updateContactRoleList (list<list<String>>updateValues) {

/* iterate through the array and update the records. The passed-in array is a multi-dimensional array that contains the Record ID and Commission % value.
*/


return ‘Records Saved’;
}

}

Put it on the page!

We have two problems, we need to access this page from an Opportunity record and need to pass the Opportunity Id. However, we are not using the Opportunity StandardController. To get around this problem we will create an Action that references a Visualforce page that will redirect to the page we created above with the Opportunity Id as a parameter in the URL.

This page will use the Opportunity Standard Controller so it can get the Opportunity Id. It will use HTML meta tags to do the redirect.

<apex:page standardController="Opportunity" standardStylesheets="false" showHeader="false" sidebar="false"><html>
<head>
<meta http-equiv="refresh" content="0;URL=/apex/OpportunityContactListUpdate2?uid={!Opportunity.Id}"/>
</head>
<body></body>
</html>
</apex:page>

When we create the action on the Opportunity Object, we will create the Action to use this Visualforce page. When the user clicks on the action this page will redirect to the list page with the Opportunity Id.

--

--