Visualforce in Practice
Visualforce in Practice
Visualforce in Practice
Michael Floyd
Don Robins
Matt Lacey
Michael Topalovich
Dan Appleman and Lior Gotersman
Ryan Sieve
Peter Gruhl
Copyrights
Copyright 20002013 salesforce.com, inc. All rights reserved. Salesforce.com is a registered
trademark of salesforce.com, inc., as are other names and marks. Other marks appearing herein
may be trademarks of their respective owners. Various trademarks held by their respective owners.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any
form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the
prior consent of the publisher.
Written by Michael Floyd, Don Robins, Michael Topalovich, Dan Appleman and Lior Gotersman,
Ryan Sieve and Peter Gruhl.
Contributions by Pat Patterson, Sandeep Bhanot, Raja Rao DV, and Michael Alderete.
Cover by Domenique Sillett.
Special Thanks to Nick Tran, Adam Seligman, Quinton Wall, Andrew Waite, Chris Wall, and
Lesley Schneiderman.
ii
Introduction
The making of this book is an interesting story. The seed of its inspiration came from developers
like you. Following one of our ELEVATE Workshops, an attendee was asked what he would like to
see next. He simply replied A book about Visualforce.
Then my colleague Mario Korf spoke up, saying that We have an Advanced Apex book,
authored by Dan Appleman to compliment our Introductory Apex Developers Guide. We should
do an Advanced Visualforce. And thats how this book was born.
But rather than follow the Beginner-Advanced documentation model, I wanted a book that
shows developers how to do real things while learning through actual apps that have been
deployed and are in practice. This is Visualforce in Practice.
I also wanted readers to get the opportunity to learn from Force.com practitioners, developers in
the trenches that are earnestly building enterprise applications and deploying them to the cloud.
I wanted to share the experience of those who have gone before you.
And so I recruited Don Robins, a veteran Force.com Certified Salesforce Trainer and Salesforce
MVPthe only developer to hold both titles. With Dons help we recruited Matt Lacey, Michael
Topalovich, Dan Appleman, Lior Gotesman, Ryan Sieve and Peter Gruhl.
Together weve assembled a collection of Visualforce example apps that you can use in your own
practice. This is not documentation, and it is not intended to document every feature built into
Visualforce. This book is intended to share the experience and expertise of our authors.
to display data using the Analytics API, and Chapter 7 walks through the process for creating
dashboards.
Chapter 8 uses the Streaming API to stream live data into Visualforce pages, while Chapter 9
shows how to build Visualforce page templates like those in the Salesforce Mobile Templates.
Chapter 10 shows you how to add JavaScript and HTML5 to your Visualforce pages, which sets
up Part III covering mobile development.
Chapter 11 walks you through the process of refactoring existing Force.com apps, then extends
to mobile the Warehouse App used in the Force.com Workbook. Chapter 12 introduces you to
the Salesforce Mobile Templates, the brainchild of Developer Evangelist Sandeep Bhanot.
And Finally Chapter 13 presents readers with tips and tricks for optimizing Visualforce pages
to ensure peek performance.
Looking back, there are many topics we would like to have covered in this book, and with the
introduction of Salesforce1 there are many new topics we would like to include in the future.
Indeed, Im looking forward to creating a second edition of Visualforce in Pracice. In the
meantime, it is my sincerest hope that you find this book not only educational, but useful in your
everyday practice.
Sincerely,
Michael Floyd
Editor in Chief, Developer Force
Salesforce.com
iv
Contents
Introduction. . ................................................................................................................................. ii
About the Book. . ........................................................................................................................... iii
Controller Extensions.................................................................................................................. 68
Navigating Between Pages Using a Controller. . .......................................................................... 69
Moving Data Between The Pages And Controller...................................................................... 71
Adding Sprints To the Project..................................................................................................... 72
Adding Milestones. . ..................................................................................................................... 75
Tidying Up................................................................................................................................... 78
Test Coverage............................................................................................................................... 79
Chapter 1
Thinking in Visualforce
T
What is Visualforce
What Can I Do with It
Controlling Views
Understanding View State
Whats new in Visualforce
Salesforce1 and Visualforce
Resources
What is Visualforce
Visuaforce is an interesting tool. If I were to define it in a nutshell Id say Visualforce is a markup
language that allows you to describe the user interface components that live on your Force.com pages.
Visualforce pages consist of two elements: Visualforce markup, and a controller. That is, you use
markup language to define the components that will live on your page and tie it to a controller
(either standard or custom) to execute the logic behind those components. Visualforce lives on the
server. So any code you write will be generated and run on the server. Lets start by understanding
the need for view state.
As youll learn in Chapter 3, a developer can either use a standard controller provided by the
Force.com platform, or add custom controller logic with a class written in Apex.
10
In terms of the MVC model, a Visualforce page involves both the View and the Controller.
All non-transient data members in the associated controller (either standard or custom)
and the controller extensions.
Objects that are reachable from a non-transient data member in a controller or controller
extension.
The component tree for that page, which represents the pages component structure and
the associated state, which are the values applied to those components.
View state data is encrypted and cannot be viewed with tools like Firebug. The view state
inspector described below lets you look at the contents of view state.
Figure 1-1. Examining view state with the inspector, available when Development Mode is enabled.
12
13
14
Optimizing your view state can improvethe performance of your pages. Understanding how
view state is affected by components is the first step. Chapter 13 provides tips and techniques for
optimizing View State for better performance.
15
Five Things You Should Master Before Getting Started with Visualforce
1. Develop a solid understanding of the platform. The basic learning path is to follow the tutorial
in the Force.com Workbook, which walks you through the construction of the Warehouse app, an
inventory management system. Not only does the tutorial guide you through and provide handson experience with every major component of the platform, the solution you create is a nearubiquitous solution that can be adapted to numerous use cases. Also:
16
Parting Words
Once you get to the point where youre building your own apps I would add Plan your design
before you begin. Salesforce Platform utilizes the classic model-view-controller model. You dont
need to follow formal design patterns, but you should map out your app and decide where you
want to implement features. And Factor in Refactoring. In my own development Ive discovered
that once youve set up your objects, added field validation, workflow rules and business logic, and
get to the user interface, theres always more logic required. Sometimes this logic finds its way into
a Visualforce button.
Now lets go learn about the Standard Controller.
18
Chapter 2
The Standard Controller
Pushing the Envelope
19
20
sObject view-related metadata controlling the configuration and presentation of list, page
and dialog views.
sObject interaction-related metadata such as actions, buttons and links defining the
common, yet potentially disparate actions across both standard and custom sObjects.
A StandardController object that is automatically provided for most standard and all custom
sObjects, bindable to a Visualforce page component with the standardController
attribute. It provides a reference to a single record, (or list of records,) as well as to a set of
common actions for data processing and default navigation.
The URLFOR() function allows dynamic generation of relative URLs though pages can be
accessed, actions performed, and processes executed. Operations result in page redirection,
and multiple arguments can be added to provide URL parameters, additional record and
sObject context, and configurable action override control.
The $Action Global Variable can be used with the URLFOR() function to invoke dozens
of different kinds of actions available to both standard and custom sObjects, independent of
any StandardController and its associated sObject.
This chapter will focus on the standard controller mechanisms provided, as well as the
configurable actions and navigation capabilities that developers can leverage programmatically.
The interaction between a user and their business data can be simple or complex, but
nonetheless requires a detailed set of instructions and rules to be managed. It is the controller that
provides such management, while preserving a clean separation between the view layer and the
data model.
In the MVC pattern, Views know how to present their data and accept input from the user, but
know nothing about how to actually fetch that data or process it.
Data Models know how to perform various operations such as Save, Delete or Merge, but need
to be told when to operate, and need to be provided with the data to operate on.
It is the Controllers job to marshal both data and action selection, as initiated by the user from
within the View, to the Model. Controllers are also responsible for responding accordingly with
navigation redirection based on the results of any particular action.
21
Controlling Data: Standard controllers fetch data and provide it to the page, list, dialog or
form, all referred to here as views. While it is the views task to manage the presentation of
its data, the controller gathers the data from the view as entered or changed by a user, and
passes it to the data model for processing.
Controlling Navigation: Standard controllers navigate the user to any specific view associated
with an action. They also handle redirection to subsequent views, based on the specific
outcome of various actions and data operations.
Developers might assume that they must resort to the implementation of custom Apex logic
to provide complex functionality that their applications require, yet might be unaware that
such functionality might already be available to them in Visualforce via standard controller
mechanisms.
It is a best practice to make sure you understand what is available to leverage in the standard
controller layer before you begin to design and build your own custom pages and controllers to
avoid reinventing the wheel.
Lets break down the standard controller into its core parts, focusing on both the common and
less obvious aspects to clarify what is automatically available in the Force.com standard controller
instruction set.
CONTROLLING VIEWS
Materializing a User Interface
The metadata-driven user interface is one of the most powerful aspects of the Force.com platform,
making development of business applications both rapid and cost effective.
Using no code, complex relational database applications can be configured and deployed in short
order because the standard user interface for each sObject, including the Home Page, List Views,
Search Dialogs, Detail and Edit pages, are all materialized by the Force.com engine at runtime, and
managed by standard controller functionality.
22
The controller mechanism utilizes each sObjects default or customized metadata, field
configuration, and other various settings available on the detail setup page.
There is a minimum metadata configuration provided by default for each standard sObject
delivered with a new org, and for each custom sObject created that includes the following:
Inclusion of a hyperlinked standard Name field in all list views and dialogs. These views can
be further configured to display additional fields.
A default page layout including all standard fields, (other than the record ID,) pre-existing
for standard sObjects and initially generated and configured for each new custom sObject.
Custom fields added to any sObject are added to page layouts by default at design time,
although the user can choose to exclude them.
During the creation of relationships on child sObjects, users can choose to include related
lists on page layouts of the related parent sObject.
During the creation of custom fields, profile-based, field-level security is defaulted to allow
read/write access to all new fields to all users of all profiles, but the user can customized
access as needed.
All of this related metadata is used by the standard controller engine to auto-render a basic
user interface with the appropriate data display or input controls, whether out of the box or
customized by a developer.
A user logs into their Salesforce org and is presented with those applications to which
they have rights. Once an application has been selected, the associated tabs are displayed.
Developers should realize that Apps and Tabs are in themselves a configurable view.
Selecting any tab (our focus will be on sObject Tabs rather than Visualforce or Web tabs),
directs the user to a set of views managed by the standard controller, (for the selected
sObject,) that follows a consistent navigation path.
The initial tab displayed is the Home Tab for that sObject, displaying a dropbox selector of
available list views, and a list of any recently viewed records. Various standard sObject Home
Tabs also display additional information, such as reports or tools, etc.
A user can select one of the available list views from the dropbox, the All view is present
by default. Or they can select to navigate to one of the listed Recent links which will direct
them to the detail view of the selected record.
Selection of a list view displays all records matching the filter and column configuration,
the default **All* view displays all records with just the hyperlinked Name field. List views
may be added or customized, allowing users to select column display configuration and list
filtering. These mechanisms are managed by the standard controller instruction set. You
might also note that this same set of list views are available to leverage with programmatic
access within a Visualforce page.
Selection of any record will navigate to its detail view, which is rendered based on the
associated sObject page layout metadata. The users access to buttons and links will be
mitigated by the standard controller based on the users permissions, or as configured in the
page layout. If there are page layout and record type assignments configured, the appropriate
layout will be used based on the users profile assignment.
23
The various buttons invoke the associated actions, associated navigation and processing,
again all managed by the standard controller instruction set.
This user experience flow might seem obvious for any user who is familiar to Salesforce. However,
our purpose is to make it clear that the control of the navigation path, the management of the data
components displayed on each view, the rendered controls, links and their associated actions, the
configuration of the display and filtering criteria of the list views; all are delivered by the standard
controller instruction set working behind the scenes as driven by the Force.com engine.
This configurable behavior, whether default or customized, is persisted in the metadata of
the associated sObject, and might also be affected by other means, such as user profile
permission configuration.
Each of the primary data views associated with a set of standard actions on any sObject can be
overridden with a custom Visualforce page. Page layouts, which are also configurable, can contain
nested Visualforce pages as well.
The best part is that the greater majority of the controller instruction set capabilities are available
to developers to access programmatically from their Visualforce pages, with or without underlying
Apex controller or extension logic.
This foundation-level understanding of what the Force.com engine does is beneficial to
guiding your development effort when building custom functionality, so lets take a closer look.
CONTROLLING DATA
Referencing a Single Record
No page, standard or custom, would be of much use to a user without some data to operate on.
One of the key tasks of the standard controller is to provide that data, and to facilitate binding a
views controls to the records fields, for either display or input.
Data binding to Visualforce components is relatively straight forward, but there is an aspect of
managing field data that is a bit more obscure.
As most developers know, we can associate a standard controller with a Visualforce page by use
of the standardController attribute on the component. We simply identify the standard or custom
sObject for the controller as follows:
<apex:page standardController=Account >
Once bound, the page associates within the context of the specified sObject. The standard
controller will provide the data from an associated record and allow the records fields to be bound
to the pages various components using merge expression syntax. This is the same merge syntax
used in formula expressions, email templates, and anywhere that field values need to be merged
for expression evaluation:
<apex:inputField value={!Account.name} />
Values of any specified fields associated with the controllers sObject are accessed using standard dot
notation. The name of the sObject is used to represent the record instance. Fields on related parent
sObjects, (whether a lookup or master-detail relationship,) can also be referenced and bound:
24
The standard controller requires record context that can be provided by including a URL
parameter named id and assigned with a valid 15- or 18-digit record ID. The record specified
by the ID must match the sObject type associated with the StandardController on the page, or an
error will be thrown when the page attempts to render:
https://naX.salesforce.com/apex/CustomAccountPage?id=001A0000005HjZL
This is slightly different than the URL pattern to access a standard detail page for any record,
which simply requires that the record id follow the Salesforce server instance name:
https://naX.salesforce.com/001A0000005HjZL
This standard URL pattern is referred to as a Frontdoor URL, and is available to invoke a view,
edit and insert of a record, as well as some other actions. Well take a closer look at this syntax a bit
later in the chapter, but for now just note that both of the above approaches require the record id
to be present in the URL.
This allows the standard controller to fetch the data for the referenced record, and makes the
values of all referenced fields available through the sObject reference for binding.
25
To make your code a bit less confusing to other developers who might wonder why the component
exists if never being rendered, binding the field to a variable component will also work:
<apex:variable value={!contact.accountId} var=accountId />
We wont go into too much detail regarding custom controllers and Apex, but its worthwhile to
simply note that this same issue might occur when using an Apex controller extension class with
a referenced StandardController when your code needs to access fields on the sObject that
have not been bound in the Visualforce page.
You can programmatically add additional fields in Apex using the addFields() and reset()
instance methods on the referenced StandardController instance passed into the extension class
constructor. The addFields() method takes a single argument of a String[] collection
containing a list of those field you wish to populate.
There are some important considerations however when using these methods.
The addFields() method must be called before referencing the sObject with the
getRecord() method.
Any pending changes to the record will be lost when the addFields() method is called.
The following example shows where the method is called in the constructor just before getting
the record:
public class DemoControllerExt {
public DemoControllerExt(ApexPages.StandardController
controller){
controller.addFields(new List<String>{accountId});
Contact contact=(Contact) controller.getRecord();
{
... }
...
You can add more fields to the list later in code and call the reset() method to refetch the record
which will include all of the desired field values specified.
26
Leveraging this mechanism allows your page to operate on a collection of the specified sObject
bound to the controller.
The recordsSetVar specified becomes a reference to a List of the sObjects, and the controller
will now have the following additional actions available to be directly bound to command
controls, (in addition to the primary set of actions available on a bound StandardController
managing only one sObject):
You also have access to two new attributes, filterId and listViewOptions for managing list
view selection and applying filters.
There are quite a few instance methods available on the Apex system-delivered class
27
Stateful Actions
Stateful actions are those available to be bound to Visualforce components directly from the
standard controller as referenced on the page by its standardController attribute, and
are dependent upon a form submission. Actions can also be submitted with AJAX calls from
JavaScript, and in both cases subsequent navigation redirection will be dependent upon the action
invoked and its outcome.
Such actions are most commonly bound to either an apex:commandLink or
apex:commandButton component, and invoked by a user clicking the control. Because these
command components are always associated with a form submission, they must always be nested
in an apex:form component or the Visualforce page will not compile at design time.
These same controls can also call AJAX processes by setting their rerender attribute to
reference the ID of some other component on the page. In this scenario, they will execute an
asyncronous call behind the scenes via JavaScript, and are typically used to render a partial page
refresh.
While AJAX is discussed in detail elsewhere in this book, suffice to say that these processes all
typically result in some form of HTTP request back to the Force.com server and are managed by
the standard controller. These requests may also contain additional URL parameters, added by use
of the apex:param component.
Developers can also construct their own URLs to execute actions or expose links to any
desired web page. Well discuss the use of the URLFOR() function that allows this programmatic
navigation when we discuss stateless actions.
28
apex:page can call an action upon load with its action attribute.
to call an action.
The following are the common actions which can be invoked in the context of the sObject
associated with the referenced standard controller:
Cancel: aborts an edit operation, and returns the user to the page where the user originally
invoked an edit or insert.
Save: inserts a new record or updates an existing record in context, and then returns the user
to the default detail page for the saved record.
QuickSave: inserts a new record or updates an existing record in context, but unlike the save
it does not redirect the user to another page but leaves the user on the page where the action
was initiated from.
Edit: navigates to the edit page for the record in context, and then whether saved or
cancelled, returns the user to the page where the action was originally invoked.
Delete: deletes the record in context, and then navigates the user to the Home Tab of the
associated sObject.
Here is an example of the direct binding syntax for each with an apex:commandButton component:
<apex:commandButton
<apex:commandButton
<apex:commandButton
<apex:commandButton
<apex:commandButton
<apex:commandButton
<apex:commandButton
29
Stateless Actions
There is an alternative mechanism available to developers for binding standard and custom
sObject actions to both stateful and stateless Visualforce components, (those requiring a form
submission or not,) as well as invoking them from custom buttons and links on standard page
layouts.
This mechanism is based on the creation of a relative URL for an HTTP request, with expression
evaluation based on the use of the URLFOR() function.
As defined in the Visualforce documentation:
This function returns a relative URL for an action, s-control, Visualforce page, or a file in a static
resource archive in a Visualforce page. This can be used to return a reference to a file contained in a
static resource archive (such as a .zip or .jar file).
31
The same action bound to a stateful command control, (which must be nested in an apex:form
component):
<apex:commandButton value=New Account action={!URLFOR($Action.
Account.New)} />
Replace target with a specified $Action, (or a hardcoded full or partial URL for simple
redirection.)
Replace id with a reference to the record to act upon, or set as null if no record
context. Take note that for some actions, (like Tab and List,) the second parameter is a
$ObjectType and not an ID, yet this is not well documented.
Inputs are optional, but can be replaced with a collection of parameter key value pairs that
need to be passed to the target action, (well look at some useful examples below.)
The optional no override argument defaults to false. You can replace no override with true
when you want to display the standard Salesforce page, regardless of whether there is an
override defined to launch a Visualforce page for the action in the sObject setup detail.
Because there is no form, there is no view state on the page, reducing page size.
You can include URL parameter key/value collections when invoking the action.
You can configure the no-override parameter to bypass an actions pre-existing override.
You can declare and invoke actions on multiple unrelated sObjects from the same page, such
as invoking actions against related parent sObjects or even sObjects not related to the one
referenced by the pages standard controller.
All of these actions leverage the built-in standard controller instruction set. Although not
accessed directly from a pages standard controller instance, they can and should still be
considered standard controller functionality.
The StatelessActionDemo Visualforce page demonstrates stateless links for a variety of
$Actions on the Account and Contact standard objects. See the comments in the page that will
direct you to assign a valid Account Id and Contact Id to two apex:variable components
so you can experiment with the actions. The page will also display the URLs generated by the
URLFOR() function for each $Action for your inspection.
In addition, you can also construct links referencing any Frontdoor URL pattern to directly launch
an insert, edit or view action on any sObject by simple URL navigation.
<apex:outputLink value={!URLFOR(/+Contact.accountId)} >
View My Parent Account
</apex:outputLink>
34
This same syntax can also be used to access documents or attachments stored in Salesforce by
constructing the appropriate URL containing the record ID of the desired target.
You will want to learn the usage for these common $Actions that you may need to execute
from a custom Visualforce page CommandButton or CommandLink, but that are not included in
the common set of directly bindable actions:
Share:
<apex:commandButton value=Share action={!URLFOR($Action.Account.
Share, Account.id)}/>
Clone:
New:
Tab:
There are dozens of $Actions available on standard sObjects, many specific to just one
sObject type. While there is available Visualforce documentation on both the URLFOR()
function and the $Action variable, it is not comprehensive, specifically with regard to
declaring parameters when invoking each $Action. Some actions are not identified in
the documentation as applicable to custom sObjects, but in fact they are - such as Tab and
Share. Others may be attributed to certain sObjects where in reality they do not work such as New on Contact, (the equivalent is NewContact.)
Many $Actions are fairly simple to use, and typically only require one additional argument
for the target record ID. However, required parameter syntax is not documented in detail,
and it is difficult to know just how to set parameter values. For example, when invoking the
Tab or List actions, theres little to tell you that the second parameter of the URLFOR()
function requires a $ObjectType or you will get an error message Invalid parameter for
function URLFOR.
35
You cant access URLFOR() and the $Action global variable in Apex they are for clientside navigation processing only. The closest equivalent in Apex is the construction of URLs
when instantiating a PageReference instance as a return value of an action method.
The syntax and usage of the URLFOR() function and its parameters can be fragile, and
some actions accessed with this mechanism have various dependencies conditionally based
on the state of the associated records data, the context of the page, or the configuration
of the associated sObject. For example, with the Share action, the associated sObjects
Organization Wide Default (OWD) settings must be set to either ReadOnly or Private,
or an error will occur when invoked. This does makes sense when OWDs are configured as
Public ReadWrite, as no sharing is necessary, therefore you would expect an error. However,
the wording of the error indicates that the user does not have appropriate permissions, so
the message to the user is ambiguous at best.
based on the provided record context. For example, if the page has no ID parameter value
provided in the URL, the Edit and Delete actions simply wont render a visible button,
and selecting View will simply refresh the existing page rather than navigate the user to the
default view page. There is also some default behavior built in that will redirect the user
to the sObjects Home page if the standard controller can not determine a specified record
context.
All in all, when required conditions are in order, the selected $Action will perform the
associated data processing and navigation on the record referenced by the page. If an sObjects
standard action has an override configured in its metadata, the appropriate override will be
performed when the action is called.
36
You can put additional parameters on the stateful command components using apex:param
components, so you can also leverage retURL with apex:commandButton and
apex:commandLink.
You are free to load up the parameters on your URL by passing a collection of value pairs, so
you may choose to use other parameters including saveURL or cancelURL.
You may embed field values to be used when defaulting values in a New action, but you will
need to research and include the field Ids which can be a bit tricky.
You can use expression evaluation to create parameter values in your collections, and there is
also a parameter that can auto-invoke a save action when New or Edit pages are invoked. You
simply add an encoded save=1 at the end of your parameter list.
A word of caution - we are stepping into the realm of what is often referred to as URL Hacking,
and you must tread carefully as there is no guarantee that the parameter values you hardcode
today will continue to be supported by the Force.com platform tomorrow. So carefully weigh the
risks of unpredictable code breakage down the road!
Remember from the loading section above, that the values available will include only those for
fields directly bound to components in the Visualforce page. If additional fields are required in
your Apex, you can apply the addFields() and reset() methods as discussed above.
37
Take note that there are a few other Apex classes available that extend the StandardController
class:
ApexPages.IdeaStandardController
ApexPages.IdeaStandardSetController
ApexPages.KnowledgeArticleVersionStandardController
Concurrency Considerations
There is an important distinction to note with regard to user concurrency between a standard page
and a Visualforce page dependent upon a standard controller.
38
The re-display the record text is hyperlinked, and when clicked will navigate Sally back to the edit
page of the record. However, this will also force a refresh so that the page will now show the newly
updated fields from Franks save operation.
Heres the distinction mentioned above. If the same scenario is played out with a Visualforce
page bound to a standard controller, the concurrency behavior will be different. If Frank and
Sally both launch an edit on the same record, and Frank saves his changes first, Sallys subsequent
save operation will result in an overwrite of Franks newly saved data. No error or warning will
be displayed to her or anyone else, and no one will know that Franks changes were silently
overwritten.
Take note that there are ways to code logic in an Apex controller extension class to check for
and manage such concurrency conflicts. We will not go into further detail here, but this is an
important inconsistency to be aware of.
CONTROLLING SECURITY
Security Context Variables - Standard vs. Custom
Unlike Apex code in custom controllers, standard controller behavior in Visualforce pages is
consistent with the default behavior of the standard Salesforce user experience with regard to user
CRUD permissions on sObjects, field-level security, and record sharing.
This simply means that the user will only have access to what is appropriate based on how their
administrator configured their permissions, and whatever record data their particular sharing
settings should expose to them.
39
Summary
The purpose of this chapter was to provide a foundation-level perspective on just how pervasive
the standard controller instruction set is in your Salesforce implementation, and how a developer
can leverage its numerous and flexible mechanisms when building custom Visualforce pages, with
or without Apex.
The key is to fully understand what its capabilities and benefits are before digging in too deeply
building custom Apex solutions.
40
Chapter 3
Just Enough Code
Introduction to Controllers and Extensions
Overriding Built-in
Functionality
Controller Extensions
& Custom Controller
Anatomy
View State
The Transient Keyword
Inner Classes
Collections
Sharing
Unit Testing
41
Overriding Permissions
Every Apex class can use the with sharing or without sharing attributes to regulate
enforcement of the sharing rules that apply to the current user. You can thus define a Visualforce
page that calls some methods that respect sharing rules and some that dont. You can do this by
creating an inner class on your custom controller (inner classes do not inherit sharing settings
from container class) or by creating an extension with different sharing settings.
Concepts covered: Custom Controllers, Standard Controllers, Extensions, With Sharing, and
Without Sharing keywords.
Objective
Create a Visualforce page that allows users to edit an Accounts Rating field value, and to see
a table of all the open- and closed-won child Opportunities (to help users decide on a rating).
The table will have four columns: Name, Amount, Stage, and close date. If the user submits the
Rating value Hot, create a Task on all open child Opportunities. Next, assign the Task to the
Opportunity owner, set its Status field value to Not Started, and set its Subject field value to
Send follow-up email to primary contact.
42
A common design pattern is to set an instance variable to the Account record you want to rate
the one defined by the parameter passed to the constructor. This variable will be useful in the rest
of your controllers code.
Account acct;
public StandardControllerExtension(ApexPages.standardController std)
{
acct = (Account)std.getRecord();
}
Standard controllers include a getRecord() method that returns the record specified in the ID
parameter in the page URL. If the URL has no ID parameter, getRecord() returns an initialized
object of whichever object type the standard controller references. In this example, the objective
is to set the rating field on existing Account records, especially those with child Opportunities.
In this case, you must make sure your Visualforce pages URL contains an ID parameter thats set
with the ID of the Account record you want to rate. If you find that the account referenced has an
ID property of null, you can assume the account is being inserted, and you can either disable the
functionality, or display an error message.
Standard controllers allow Visualforce page markup to reference their associated object using the
{!object} syntax. This lets you reference the Accounts Rating field by writing {!Account.
Rating}, as shown in Listing 3-1. You can also use the standard controllers built-in save method
<apex:page standardController=Account
extensions=StandardControllerExtension>
<apex:form>
<apex:pageblockButtons >
<apex:commandButton action={!save} value=save />
</apex:pageblockButtons>
<apex:inputField value={!Account.Rating} />
</apex:form>
</apex:page>
So far, your Visualforce page enables users to modify and save an Accounts Rating field.
43
When your Visualforce page loads, it will expect your extension controller to have a property
called childOpps with a getter method -- otherwise, when you try saving your Visualforce pages
markup, youll see the following error:
Error: Unknown property AccountStandardController.childOpps
This method implements a property using a special syntax. Naming a method using the syntax
getIdentifier or setIdentifier creates a getter and setter method. Its up to the method to
retrieve or set the data. The code in Listing 3-3 is essentially creating an Apex property named
childOpps with a getter method, allowing the Visualforce page to evaluate {!childOpps}.
The final piece of functionality for this extension controller creates a Task associated with any
open child Opportunities after the user selects the Hot rating and clicks the save button. By
default, the save button invokes the standard controllers save method when clicked. The save
button in this example will need to either invoke a new method or an overridden save method
containing code that implements the new functionality.
Listing 3-4. Apex method that creates the task associated with open child
Opportunities.
private void createTaskOnChildOpps() {
List<Task> tasksToInsert = new List<Task>();
for (Opportunity opp : childOpps) {
if (!opp.isClosed) {
tasksToInsert.add(
new Task(
WhatId = opp.Id,
OwnerId = opp.OwnerId,
ActivityDate = Date.today() + 3,
Status = Not Started,
Subject = Send follow-up email to primary contact
44
}
}
if (tasksToInsert.size() > 0) insert tasksToInsert;
In the code shown in Listing 3-4, if the Accounts Rating field is set to Hot, the
createTaskOnChildOpps() function is called to perform the Task creation.
45
The property, named acct returns the current account. It must be declared as public to be
referenced from your Visualforce page. The {get;set;} syntax is a shortcut for creating getter
and setter methods for the acct property, and automatically handles the storage and retrieval of the
object.
To set the acct value, first grab the key value of the id parameter in the page URL using
record where the ID equals the ID in the URL. Make sure youre querying all the fields your
controller or Visualforce page will referenceotherwise youll see an error, such as:
You can implement the childOpps property in the custom controller the same way as in the
extension example, but Listing 6 shows a more powerful and elegant alternative.
Listing 3-6. Implementing the childOpps property in a custom controller.
public List<Opportunity> childOpps {
get {
if (childOpps == null)
{
childOpps = [Select Name, Amount, StageName, CloseDate,
OwnerId, IsClosed
From Opportunity
Where AccountId = :acct.Id
and (IsWon = true or IsClosed = false)];
}
return childOpps;
}
set;
}
This approach uses the {get;set;} shortcut to implement its getter and setter methods,
and define a property called childOpps. The shortcut improves your codes organization and
readability.
This example uses a design pattern known as lazy loading. By only fetching the Accounts
child Opportunities when the childOpps property is null, you prevent the accessor code
from running each time your controller references the property. Lazy loading makes your
Visualforce page run more efficiently. (See Chapter 14, Performance and Best Practices for
more information on Lazy Loading.)
46
When making a SOQL query without adding a LIMIT clause, your code runs the risk of
exceeding governor limits. Add a LIMIT clause thats higher than the record count you
expect the query to return and lower than the governor limit of 50,000.
If youre displaying multiple records in a Visualforce page, add pagination. Pagination allows
you to cut down the number of records retrieved on a given page. Without pagination, you
might end up displaying a collection of possibly tens of thousands of records, putting you at
risk for slow page loads, an over-sized view state and a dizzying visual experience.
Use custom objects or custom fields on sObjects to aggregate data and allow queries to fetch
fewer records. Say you have a Visualforce page that renders a line chart of the average Tasks
created per day for the past year. Making a SOQL query that fetches all of these Tasks is
likely to exceed limits. You can create a custom object named Daily_Data__c that contains
aggregated data per day. You can schedule a daily batch process that populates a custom field
on the Daily_Data__c, object called Tasks_Created__c, with the number of tasks created
that day. Now your controller can query the new custom object and only have to fetch a
maximum of 365 records per year.
Instance Variables, Apex Properties, Getter and Setter Methods, Access Modifiers, SOQL Limits,
Lazy Loading, Overriding standard controller actions, ApexPages uses, How to select controller type.
<apex:page controller=CustomController>
<apex:form>
<apex:commandButton action={!buttonClicked} value=Click
Here/>
{!clickCount}
</apex:form>
</apex:page>
48
Properties loaded with large collections in your controller not directly referenced by your
Visualforce page are good candidates for the transient modifier. Consider whether youll need to
recreate the property every time the page reloads, say, when a user invokes a method by clicking a
button. Including the property in the view state eliminates the need for your controller to re-create
it on every page load. Youll have to decide whether its better for your application to have a larger
view state or have more processing done on each page load.
The Salesforce Winter 14 Release is piloting (at the time of this writing) a new feature that
allows a Visualforce pages view state to be stored on the server rather than the client, eliminating
the need to send the view state back and forth between the two. This feature resolves some of the
issues raised in this section by reducing the amount of data transferred with each page request.
The new feature is especially useful for mobile apps. These apps run on devices that are more
likely to have low bandwidth or high latency. Because the view state currently gets sent back and
forth between the server and client, bandwidth and latency can affect the speed of this interaction.
(See Chapter 14 for more details.) When the view state is only stored on Salesforces server, no
back-and-forth is required, freeing up bandwidth for other functions.
Concepts Covered: View State, Form tags, Transient keyword
Inner Classes
Inner classes play a large role in Visualforce development, perhaps more than in any other area
of Salesforce development. Besides improving the organization of your code, inner classes let you
display a collection of regular objects (non-sObjects) in standard list components and make it easy
to support more flexible sharing settings.
49
Collections of Objects
Say you want to add a new column to the table in the example code that displays an Accounts
child Opportunities. This new column will indicate the number of closed Tasks per Opportunity.
The problem is that this value is not a field on the Opportunity object. Nor can you create a roll-up
field counting closed Tasks. Without inner classes, the solution to this problem would be rather
complex.
In the previous example, childOpps, a list of Opportunities, was associated with the value
attribute on an <apex:pageblockTable> tag. Instead of passing a list of Opportunities, you can
pass a collection of objects created from an inner class defined in your controller. This object will
function as a wrapper class for Opportunities and contain an additional property that holds the
closed Task count, as shown in Listings 3-8 and 3-9.
Listing 3-8. This Apex Controller Acts as a wrapper class for Opportunities.
public List<OppWrapper> childOppWrappers {
get {
// Accessor code here
}
set;
}
public class OppWrapper {
public Integer closedTaskCount {get;set;}
Opportunity opp {get;set;}
public class OppWrapper(Integer p_closedTaskCount, Opportunity
p_opp)
closedTaskCount = p_closedTaskCount;
opp = p_opp;
}
}
50
Sharing Settings
Inner classes do not inherit sharing settings from their container class. This means you dont have
to create a new extension controller with a different sharing mode in order to add functionality
with different sharing settings.
Say you have a Visualforce page thats associated with a controller that doesnt enforce sharing
rules. You define the class using the without sharing attribute, as follows:
public without sharing class CustomController {
}
You want the page to implement functionality that everyone in the organization, regardless of
permissions, should be able to use. But lets say theres one method that creates an Opportunity on
an Account and you want to enforce DML insert permissions when its invoked. You can do this
using an inner class, as shown in Listing 3-10.
Listing 3-10
public without sharing class CustomController {
public CustomController(){
}
public with sharing class withSharingClass {
public void insertOpp() {
// Code that inserts Opportunity
}
}
The same pattern can be used for any case where you want to control where sharing rules apply.
Use cautioncareless use of the without sharing attribute can lead you to revealing data that
you really want to keep secure. In most cases, youll use the inverse of this exampledefining
your outer class as with sharing, while using an inner without sharing class to allow access to
selected data.
Concepts Covered: Inner Classes, Sharing Settings, Wrapper Class, Overriding standard
controller actions
Unit Testing
Custom controllers and extensions, like any Apex classes, need to be covered by unit tests. Dont
approach unit test design with the sole goal of meeting the code coverage requirement. Your
goal should be to create testing scripts that interact with your code the same way users or other
programs will interact with your code when its live on a production organization. For controllers,
this means you should think about all the different ways you expect users to interact with your
Visualforce page. Test your expectations about what the controller should output based on the
kind of data users will submit or methods theyll call. Try having each of your test methods test for
one specific function that your controllers provide. If you write these kinds of functional unit tests,
code coverage will be almost always be a non-issue.
51
52
53
Custom controllers dont have any arguments passed in their constructor. The custom controller
in the example code uses the id parameter in the page URL to fetch the Account record. This
means that in order to test the custom controller, you need to set the current page to the controlled
Visualforce page, set the id parameter, using setCurrentPageReference() to set the page
reference, and the getParameters method to access the pages parameter collection. After
creating Account records and child Opportunities, set the pages ID parameter to the Accounts ID.
The rest of the testing code is the same as the extension controller unit test code.
Concepts Covered: Unit tests for custom controllers and extensions, Test.startTest() and
Conclusion
Visualforce makes it easy to create interfaces for users to interact with the Force.com platform. In
this example, you learned that with less than 100 lines of code, you can make a Visualforce page
that allows users to edit an Accounts rating, saw a table of child Opportunities, and created Tasks
on the open child Opportunities.
As long as your code handles few records, users, and UI components, Visualforce development
isnt hard. However, poorly designed controllers can easily exceed governor limits or slow down
your applications performance. Just referencing a property more than once, not adding a LIMIT
clause in a SOQL query, or not managing your view state size can cause your application to
slow down, or fail. Governor limits force developers to optimize their custom controllers and
extensions at design time. Designing a sound architecture that scales will keep your application
performing well when handling edge cases you might not have expected. One of the best tools
for optimizing your controllers is to write unit tests that handle all the likely states your program
might find itself in.
The Apex language is very similar to other block-structured languages. With an understanding
of a few concepts and keywords, along with basic optimization techniques and unit testing,
youll be able to implement virtually any functionality you need, for both desktop and mobile
applications.
54
Chapter 4
The Standard List Controller
L
Displaying Standard
Lists of Records
Customized List Displays
Adding Pagination
Filtering with List Views
Mass Updates of Records
Providing Feedback
If youre looking for a standard-style list of related records for a parent record, then the easiest
route is to use, unsurprisingly, <apex:relatedList>. At this point were dealing with a specific
record, i.e. the parent of the children we want to display, and as such a StandardController must
be used and a suitable record ID provided in the URL. If a record ID is not provided then nothing
will be displayed as the StandardController will provide a new record that necessarily has no
children.
<apex:page standardController=Account>
<!--This page must be accessed with an ID parameter
e.g. /apex/RelatedList?id=001i000000McLJl -->
<apex:relatedList list=Contacts/>
</apex:page>
Building everything from scratch can prove a little tiresome, so if you dont need such a detailed
level of customization, it makes more sense to use Visualforce components that do some of the job
for you. When it comes to displaying lists of records, the usual suspects are <apex:dataList>,
<apex:dataTable> and their styled sibling <apex:pageBlockTable>.
<apex:dataList> is the most basic of the offerings, it simply generates a standard HTML
unordered list, with a list item for each record encountered; the upshot of this is that its great
when working on pages with custom styling, but looks somewhat shabby when used in a page
built with standard UI components.
56
Paginating Lists
In a similar fashion, <apex:dataTable> generates basic HTML table output without
applying any styling to it, displaying the data without any borders or styling making it great for
customisation with CSS. Its counterpart <apex:pageBlockTable> is intended for pages that
leverage the standard look and feel of the system, immediately stretching the table to fill the width
of the containing block, adding borders, highlighting on mouse over and shading for column
headers.
The following complete page presents a list of contacts to the user, in a standard table with their
ID, name and email address.
<apex:page standardController=Contact recordSetVar=contacts>
<apex:pageBlock title=Simple Contact List>
<apex:pageBlockTable value={!contacts} var=c>
<apex:column value={!c.Id}/>
<apex:column value={!c.FirstName}/>
<apex:column value={!c.LastName}/>
<apex:column value={!c.Email}/>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:page>
This creates an attractive table of the information and if there are enough contacts in the org, then
it will display the first 20 records by default. Since were outputting fields from an object and using
<apex:pageBlockTable>, the column headers are automatically created using the labels for the
fields in question.
Finally, be aware that these Visualforce tags work just as effectively for displaying related records,
so if we wanted to display an Accounts basic information with the related Contacts below we
might do the following:
<apex:page standardController=Account>
<apex:detail subject={!Account.Id}/>
<apex:dataTable value={!Account.Contacts} var=c rows=2>
<apex:column value={!c.FirstName}/>
<apex:column value={!c.LastName}/>
</apex:dataTable>
</apex:page>
Adding Pagination
When working with general lists there is no filtering by default, meaning the page will
have access to every record in the system. As noted, the default size of the list displayed is
20 records. This can be increased (using the rows parameter on <apex:dataTable> and
<apex:pageBlockTable>), but to provide the user with a sensible interface its important
to add pagination controls. Pagination is easy to implement using List Controllers and the
documentation for its Apex equivalent, the standardSetController class, provides some
insight into how it works. The controller methods include GetNext() and GetPrevious() that
allow jumping between pages, and the methods GetHasNext() and GetHasPrevious() that
let you quickly determine whether there is a page available either side of the current one.
57
Its important to note that although we can control the number of rows displayed with
<apex:pageBlockTable> and other tags, the size specified does not carry over to the effective
page size of the controller. In other words, if you specify rows=5 then your table will initially
display rows 1 through 5 of the 20 in the controllers current page. Clicking the Next link
(assuming there are more than 20 relevant records in the system) would then refresh the table and
show records 21 through 25 from the next page of 20.
The documentation states that a custom controller extension is needed to change the size of
pages displayed, however that is not the case! In order to change the number of records per
controller page we must alter the PageSize member variable of the controller. We can extend
page size as an option to the end user by providing them with a picklist of various values, and by
adding an <apex:commandButton> with a NULL action to force the controller to update. The
code below demonstrates this technique, and replaces the two <apex:commandLink> lines in the
previous example. Note the addition of an <apex:panelGrid> to help keep everything spaced
out, and an <apex:outputPanel> to group together the label, picklist and button in the third
column of the grid.
<apex:panelGrid columns=3 cellspacing=16>
<apex:commandLink action={!Previous} value=Previous Page
rendered={!HasPrevious}/>
<apex:commandLink action={!Next} value=Next Page
rendered={!HasNext}/>
<apex:outputPanel>
<apex:outputText value=Records per page: />
<apex:selectList value={!PageSize} size=1>
<apex:selectOption itemValue={!10} itemLabel=10/>
58
Paginating Lists
<apex:selectOption itemValue={!20} itemLabel=20/>
</apex:selectList>
<apex:commandButton action={!NULL} value=Update/>
</apex:outputPanel>
</apex:panelGrid>
Last but not least, another nice feature thats trivial to add is an indication to the user of which
page theyre viewing and how many there are in total. The first part is trivial: the variable
PageNumber represents the page the user is currently viewing.
The ResultSize member variable indicates how many records are in the list in total, and as
we know PageSize indicates the number of records displayed per page, so we can divide one
by the other in order to obtain the number of pages available. There is one potential issue with
this division however: if there are 10 records to a page and 16 to display, we wouldnt want to tell
the user that theyre on page 1 of 1.6, or worse yet, 2 of 1.6! This can be addressed by use of the
CEILING() Visualforce function, which ensures any fractional parts are rounded up.
59
When you view this page in the browser, the first thing youll notice (and hopefully expect) is that
the table is empty; we havent selected any records anywhere. So the list of selected records in the
controller is empty. The first step is to go to Setup and navigate to Customize > Contacts > Buttons,
Links and Actions.
Tip: You can quickly find links using the Search box at the top of the navigation panel.
Click New Button or Link, which will open a window similar to Figure 5-1 and enter sensible
values for the label and name; Change Account would make a good label for this page, the name
will be populated with a default value automatically after typing the label. Choose List Button
as the Display Type, and then Visualforce Page for the Content Source. Now choose the
page you just created from the Content picklist. The only pages available for selection will be those
that use the Contact StandardController and a recordSetVar parameter to indicate that they
support lists of data. See Figure 5-1 for a completed form.
Once the button is created we want to ensure that we have a list of Contacts to work with
somewhere within the standard user interface, and a one way to do this is to add the Contacts
related list to the Account page layout. When the section has been added (and before the page
editor has been closed) we can then add our new button to the related list, which in turn modifies
the list so that users can select the records that they want to work with.
With the button added and the layout saved, select a few checkboxes alongside some Contacts on
an Account, and then click the button added to return to the Visualforce page once more. Now the list
should contain the selected contact records and we can consider how to update them.
As already mentioned, we are still using the StandardController and that provides a master
record for us to work with: all we have to do in order to update our list of selected records is
60
Paginating Lists
specify the desired values on that record and then call the Save action to update them. You may have
guessed from the table and the name of the page, but the field well be updating is the Account field,
allowing us to move multiple Contact records from one Account to
Figure 5-1: Filling in values for the New Button or Link form.
another at once. Above the list of records we will insert a single input field and two buttons: one to
call the Save action and another to call the Cancel action which will let the user back out if they
decide they dont want to update the records after all.
<apex:page standardController=Contact recordSetVar=contacts>
<apex:form>
<apex:pageBlock title=Account Update>
<apex:pageBlockButtons location=bottom>
<apex:commandButton action={!Save} value=Update
Contacts/>
<apex:commandButton action={!Cancel} value=Cancel/>
</apex:pageBlockButtons>
<apex:pageBlockSection>
<apex:inputField value={!Contact.AccountId}/>
</apex:pageBlockSection>
</apex:pageBlock>
<apex:pageBlock>
<apex:pageBlockTable value={!selected} var=c>
<apex:column value={!c.FirstName}/>
<apex:column value={!c.LastName}/>
<apex:column value={!c.Account.Name}/>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:form>
</apex:page>
61
To use this controller in a page we need to use the controller parameter, as opposed to the
standardController, which weve been using up until now. The list of records can be accessed
using {!ssc.records}, but there is a catch: the list of records returned is a list of generic
SObjects (the base type for other objects) so if you attempt to reference a field directly, such as
with {!a.Name} youll receive a compiler error. There are two ways around this; the first is to add
a controller method that casts the list to the correct type:
public List<Account> GetAccounts()
{
return (List<Account>)ssc.GetRecords();
}
The second method is to use dynamic binding in the Visualforce page, whereby the name of the
field is referenced using a string, e.g. {!a[Name]}.
Regardless of the method you choose, if you try to output a field thats not been queried for, you
will receive an error at run time (when you try to view the page), so it pays to be careful about
which fields youre using.
By making the member variable for the StandardSetController public accessing its various
properties in the corresponding Visualforce page is trivial, so pagination etc. works exactly the same
way as it did in the first part of this chapter, you just have to prefix all of the references with ssc..
62
Paginating Lists
Recall that Apex getter methods that are called from Visualforce pages must start with get even
though that prefix is discarded inside the page mark-up itself; because our set controller is public we
call its getter methods directly as shown in the pagination links in the code below.
<apex:page controller=CustomAccountListController>
<apex:form>
<apex:pageBlock>
<apex:pageBlockTable value={!ssc.Records} var=a>
<apex:column value={!a[Name]}/>
<apex:column value={!a[Rating]}/>
<apex:column value={!a[NumberOfEmployees]}/>
</apex:pageBlockTable>
<apex:commandLink action={!ssc.Previous} value=Previous
Page rendered={!ssc.HasPrevious}/>
<apex:commandLink action={!ssc.Next} value=Next Page
rendered={!ssc.HasNext}/>
</apex:pageBlock>
</apex:form>
</apex:page>
When you view this page you should see a list of up to 500 accounts, and the next step is to gather
input that we can use to filter the records selected from the Database. As in the bulk update
example well place a form above the list of records to gather the users input. To filter according to
specific field values, a public Account member variable facilitates input since it allows the use of
<apex:inputField>, but since were using a custom controller there are other options available.
If, for example, the goal of the page were to allow the user to view Accounts according to their
headcount, instead of asking the user to type in a number, and potentially receiving bad input
as part of the process, it would make better sense to provide pre-defined options that align with
the companys definitions of Account size; as you may well have guessed the tag to use in the
page is <apex:selectList> and it can be populated with options directly in the page as seen
previously, or the options can be provided by our controller.
Since were taking action in code based on the value selected, it would be wiser in this case to
have the available options listed in the controller so that when it comes to maintaining the page we
only have to look in one place, not two. So, first add two member variables for storing the list of
options and the chosen value:
public List<SelectOption> sizeOptions {get; private set;}
public String chosenSize {get; set;}
Then add the following code to the constructor in order to initialise the list of options and default
the selected value when the controller is instantiated:
sizeOptions = new List<SelectOption>
{
new SelectOption(small, Small),
new SelectOption(medium, Medium),
new SelectOption(large, Large)
};
chosenSize = small;
63
Next we need to create a new custom action that the Visualforce page can call to actually change
the filter being used, as right now were simply providing a mechanism for gathering input but not
doing anything with it. The controller method to do this is simple; it simply builds a new SQOL
query and creates a new instance of the StandardSetController class. The reasoning behind
building up the query as a string, as opposed to having three if statements that each create a new
controller, is that should the page require another filter in the future itll be far easier to add in.
public PageReference ApplyFilter()
{
String query = Select Id, Name, Rating, NumberOfEmployees from
Account where ;
if(chosenSize = = small)
{
query += Employees <= 100 ;
}
else if(chosenSize == medium)
{
query += Employees > 100 and Employees <= 500 ;
}
else
{
query += Employees > 500 ;
}
query += limit 500 ;
}
The last step is to fire this action from the page, and rather than using an
<apex:commandButton> or <apex:commandLink> as we have done in previous pages,
well utilize the <apex:actionSupport> element so that the page will automatically refresh
when the user changes the selected value. This tag supports the action parameter as youd
expect, but another required parameter is called event, and the value it takes should be the name
of the JavaScript event that we want to hook an action to. (For more details see Chapter 10,
Using JavaScript and Visualforce.) For this scenario well be using the OnChange event, so the
<selectList> should updated accordingly to reflect the next code snippet.
64
Paginating Lists
<apex:selectList value={!chosenSize} size=1>
<apex:actionSupport action={!ApplyFilter} event=onchange/>
<apex:selectOptions value={!sizeOptions}/>
</apex:selectList>
Now if you test the page out you should find when you change the value in the picklist the table
will update to reflect the matching accounts. You may have noticed that the initial view shows
all accounts, and doesnt match the preselected filter option. The fastest solution is to modify our
constructor and call the ApplyFilter() inside it so that were showing accurate data from the
offset.
public CustomAccountListController()
{
sizeOptions = new List<SelectOption>
{
new SelectOption(small, Small),
new SelectOption(medium, Medium),
new SelectOption(large, Large)
};
chosenSize = small;
ApplyFilter();
}
to update correctly we need to specify a rerender target for the action, and to that end the
<apex:pageBlock> now has an ID which is used for that. The upshot of all this that when the
user chooses a new value, we can hide the picklist, show something else (the text Please wait in
the next example) while the action is in progress, and then switch back after. Additionally, because
were now using a rerender target the experience is less jarring as the page no longer refreshes in
its entirety.
<apex:pageBlock id=theList>
<apex:pageBlockSection>
<apex:actionStatus id=filterStatus startText=
Please wait...>
65
One other area in which our page is lacking is in providing useful feedback when there are no
records to display. You may or may not have seen this depending on the data in your org, but with
the bundled data in a Developer Edition org you should find there are no accounts to be seen
when choosing Small from the picklist. Displaying an empty table isnt much use, and due to the
fact that the headers also go missing it does give the impression that there was an error when there
wasnt.
To alleviate such symptoms well hide the table when theres no data to display through the use
of the rendered parameter. This parameter takes a boolean value and that can be the result of an
expression, so all we need to do is check whether the list exists by comparing it to null.
<apex:pageBlockTable value={!ssc.Records} var=a rendered={!ssc.
Records != null}>
In addition to hiding the empty table we can put a message onto the page to inform the user that
no matching records were found, this could be a static message but it can also be controlled by the
custom controller; well take this option as itll be more flexible should we want to display other
messages in the future. The first step is to insert an <apex:pageMessages> element inside the
<apex:pageBlock>:
<apex:pageBlock id=theList>
<apex:pageMessages/>
Next we need to check the count of the results obtained when ApplyFilter is called, the change is
below.
ssc = new ApexPages.StandardSetController(Database.Query(query));
if(ssc.GetRecords().Size() == 0)
{
ApexPages.AddMessage(new ApexPages.Message(ApexPages.
Severity.Info, No matching Accounts found.));
}
}
return null;
Revisit the page to test this functionality, and you should see a nicely formatted yellow
information message when choosing a filter with no matching results.
66
Chapter 5
Building Wizards With Visualforce
O
Controller Extensions
Navigating Between Pages
Moving Data Betwen
Pages and a Controller
Example: Agile Sprints
Calling DML in a Controller
Figure 5-1:
Once youve built up the same data model, its time to start creating our pages.
Controller Extensions
There are two main ways of creating Apex-based controllers for Visualforce pages, the first being
controller extensions and the other, custom controllers. As you might have guessed from the
name, a controller extension extends a standard controller, and provides functionality for what is
already provided by the system. Youll recall that standard controllers offer the basic actions that
are common throughout Salesforce, creating new records, editing them, cancelling edits or saving
changes, etc. They automatically handle the loading of existing records based on an ID parameter
passed in the URL.
Custom controllers come with none of this pre-built functionality, and its up to you as the
developer to write all of it. Since our hierarchy of objects has project at the top, were going to take
advantage of the Project__c standard controller to save some work, despite the fact that well
still have to provide such functionality for the other objects were dealing with.
68
Wizards
Since the first page will be gathering input related to the Project__c record that will be created,
we can go straight ahead and add some input fields since we have the luxury of working with the
standard controller. The following code snippet goes between the page block tags of the first page
to provide input fields for the name field and the two custom fields defined.
<apex:inputField value={!Project__c.Name}/>
<apex:inputField value={!Project__c.Start_Date__c}/>
<apex:inputField value={!Project__c.Budget__c}/>
Now that the pages are in place, we can create the skeleton for the controller extension. Create a
new Apex class and give it the name ProjectCreateExtension, so that its clearly related to the
pages and also makes it immediately obvious that its a controller extension, without needing to
view the source.
In order to access the standard controller inside our extension, we need to define a constructor
for the class, which is a method that returns no values but is responsible for any setup that must
be done when an instance of the class is created. Controller extension constructors must take
one argument thats a reference to the standard controller created by the system when we view
a page. This means that the functionality offered by the controller will be available to us inside
the constructor, but nowhere else, so well also add a private variable (meaning a variable only
accessible inside the class) that will store the reference provided.
public class ProjectCreateExtension
{
private ApexPages.StandardController sc;
public ProjectCreateExtension(ApexPages.StandardController
standardController)
{
// store a reference to the standard controller
sc = standardController;
}
}
69
Once the methods are in place and the code has been saved without error, we can return to the
first page and add a button that triggers the action to navigate to Page 2. As usual with Visualforce
pages that mimic the standard look and feel of the system, the button will be nested inside
<apex:pageBlockButtons> tags that go immediately inside of the <apex:pageBlock> itself:
<apex:pageBlock title=Project Creation>
<apex:pageBlockButtons location=bottom>
<apex:commandButton action={!ToPage2} value=Continue/>
</apex:pageBlockButtons>
If we try to save the page at this point well be rewarded with an error to the effect that ToPage2
is an unknown method, and thats because all the page knows about at the moment is the standard
controller we previously specified. We inform a page of our extension by using the extensions
parameter in the <apex:page> tag:
<apex:page standardController=Project__c
extensions=ProjectCreateExtension title=New Project>
Once thats been added, the page should save with no errors, and once it has, its time to
give it a quick test. To access it quickly, you can either manually navigate to the URL for the
page (<instance>.salesforce.com/apex/CreatePage1. For example, cs1.salesforce.com/apex/
CreatePage1), or click the Preview button in the top right corner of the window if youre
using the Developer Console. To make it easier in the long run, well create a custom tab for the
Project__c object. Inside the setup screens, navigate to Build > Create > Tabs. Click the New
button for Custom Object Tabs, choose the Project object from the picklist, and pick a style for the
tab. Click Next, Next again, and then Save. You can add the new tab to your tab bar by clicking the
+ sign in the bar and then the customize button.
70
Since the page will be used to create new Project__c records and uses the Project__c
standard controller, we are also able to override the New button for projects. Navigate to the
configuration for the Project__c object under Build > Create > Objects and scroll down to the
section labeled Buttons, Links and Actions. Click the Edit link on the New row and then on the
next page choose Visualforce Page from the radio buttons. The picklist on the screen will update
to show all Visualforce pages using the Project__c standard controller, so choose ProjectCreate1
and save the change. Now the page is easily accessible by navigating to the new Projects tab and
clicking the standard New button.
Wizards
When you navigate to the page, you should see it as shown below, and since none of our fields
are marked as required in the custom object setup, clicking the Continue button should take you to
the next page with the page block labelled Sprints. So far, so good!
Figure 5-2:
Now that Page 1 is looking good, we can focus on the second page, so modify this page to add
a button labelled Project Details that will return the user to Page 1. Were not going to add
navigation to the final page yet as that will be a little more complicated; that page will be used for
adding stories to sprints, and so will need to know which sprint is being worked on. Before we can
focus on that, we need to provide the user with a way to create milestones.
Note that although the string variable itself is private, because the getter method is public, the
value of the variable is accessible in the page. If you want to give this a try and display this variable
in your page, you cant use <apex:outputField> as its not a field on an object, but you can use
a more specific tag called <apex:outputText>, which works in the exact same way:
<apex:outputText value={!hello}/>
71
In a fashion corresponding to displaying the variable, you can collect input from the user to assign
to the variable through an <apex:inputText> tag:
<apex:inputText value={!hello}/>
At this stage, it probably seems like theres a lot of overhead involved in allowing pages to access
variables in the controller, so youll be relieved to know that the developers of the platform
provided a neat shortcut to take the pain out of dealing with basic functionality. To declare a string
variable as being readable and writable from pages, you can use the syntax below to replace both
of the two explicit methods above.
Public String {get; set;}
To ensure the page can only read or only write, you can declare either the getter or the setter to be
private, making them hidden from the page.
// read only string
public String hello {get; private set;}
The downside with using the shorter syntax is that you cant assign a value to the variable when
you declare it, so instead, you need to do so elsewhere, and the constructor can be a good place to
do this in simple classes:
public ProjectCreateExtension(ApexPages.StandardController
standardController)
{
sc = standardController;
hello = Hello, World!;
}
Wizards
The system method ApexPages.CurrentPage() returns a page reference for the current
page. Calling GetURL() on that reference will let us know the URL of the current page. We can
leverage this to ensure that well only save the project record when the current page is Page 1,
though its likely to contain a lot of information we dont care about. We ignore that information
by using the String method StartsWith() to just check whether the beginning of the URL is a
match, and also ToLowerCase() to convert the string to lower case, meaning we need not worry
about case mismatches:
public PageReference ToPage2()
{
if(ApexPages.CurrentPage().GetURL().ToLowerCase()StartsWith(/
apex/projectcreate1))
{
// Save code will go here
}
return Page.ProjectCreate2;
}
At the beginning of this chapter, there was a brief discussion about the functionality provided by
standard controllers. Recall that they can be used to save records. Also, we saved a reference to the
standard controller inside of our extension, which means its now available for us to use whenever
we need to do so. When adding a command button to a page to save a record using the standard
controller, the action used is called save, and this corresponds directly to a save method provided
by the Apex class StandardController. Thus, the final change we need to make to the action
method ToPage2 is to call that method:
public PageReference ToPage2()
{
if(ApexPages.CurrentPage().GetURL().StartsWith(/apex/
CreateProject1))
{
sc.Save();
}
return Page.ProjectCreate2;
}
Now that saving the project has been taken care of, were ready to go about the task of creating
the interface that will allow the user to add sprints to our project. The form this page will take
will comprise a form for collecting sprint information at the top, and a list of those already added
below. The form itself is nothing new and should need no explanation; the one difference between
this and the project page is that were not using the standard Sprint__c controller, so we need to
create a public Sprint__c variable in the controller to write information to. The form input fields
use the name of this variable rather than Sprint__c.
Creating the variable in the controller is simple and uses the getter syntax we saw earlier.
Well place it under our private variable used to hold the standard controller reference:
73
Dont forget, this variable needs to be initialized to hold a new Sprint__c object, so well take
care of that inside the constructor:
// create a new sprint instance to collect user input
sprint = new Sprint__c();
Now build up Page 2 in the same manner as Page 1, but use the sprint variable name to
indicate to the page that thats where information is to be stored (note, if you didnt add the
extensions=ProjectCreateExtension attribute to Page 2 earlier, youll need to do so
now).
<apex:pageBlock title=Sprint Details>
<apex:inputField value={!sprint.Name }/>
<apex:inputField value={!sprint.Start_Date__c}/>
</apex:pageBlock>
Saving the information for the sprint record is a little different from doing so for the project. We
dont have the Sprint__ c standard controller to work with, so we must define our own save
action method, which can be called from the page. To store the users input in the database, we
use whats known as a DML (Data Manipulation Language) operation, Insert. Insert is one of four
such operations, the other three being update, upsert (which combines updating existing records
and inserting new ones), and delete.
Because this object is a child on a master-detail relationship, we must ensure that the lookup to
the parent is set before trying to save it or well receive an error from the system. When we saved
the project record, it was updated with its new ID, and we can retrieve that from the standard
controller using the method GetRecord() to get a reference of the record itself. Because the
StandardController class can work with any object type, the GetRecord() method returns a
generic SObject. Theres no need to worry about the details, but you should know that when using
this method, you need to tell the system what type of object the result should be interpreted as.
This is done by prefixing the call to the method with the type we want in brackets. This is known
as type casting.
Project__c project = (Project__c)sc.GetRecord();
sprint.Project__c = project.Id;
update sprint;
We want the user to be able to create multiple sprint records for a single project, so after saving the
record, well keep a copy of it in a list in the controller extension so that we can keep track of all
the records entered.
The list is stored in a controller member variable. The declaration looks like the following and
should be placed with the other variables at the top of the class.
public List<Sprint__c> sprints {get; set;}
The List class is known as a collection object, and is capable of representing lists of any type of
object, which is specified in the angle brackets after the List class name. Once more we must create
an instance of this list and assign it to the variable inside the constructor.
74
Wizards
// create a new list to store the sprints added by the user
sprints = new List<Sprint__c>();
When we insert the record, the information is saved and our variable updated with the unique ID
assigned to it. Well then use the Add method on the List class to add the new record to our list,
and finally create a new Sprint__c instance so the user is presented with a clean form for adding
the next record. The method must return a null PageReference to make the page refresh when
the action is called; if we dont do this, the user will not see empty fields ready for the next record.
The full body of our custom save method is below.
public PageReference SaveSprint()
{
Project__c project = (Project__c)sc.GetRecord();
sprint.Project__c = project.Id;
insert sprint;
sprints.Add(sprint);
sprint = new Sprint__c();
}
return null;
Once youve added this method to the extension, youll need to add a command button to Page 2,
wrapped in <apex:pageBlockButtons> tags, in the same way as for Page 1, though the action
attribute should reference the new save method using {!SaveSprint}.
At this stage, if you test the page by starting on Page 1 and press continue, you might find the
lack of feedback when saving sprints somewhat disconcerting, and end users most definitely
would. Since a user is going to want to know what theyve already added to a project, we can
display the list of sprints being built up in the extension in another page block below the form,
and <apex:pageBlockData> is the perfect tool for such a job. More information on this tag can
be found in Chapter 4, though essentially, it loops over a list of records, outputting a table row for
each, where the columns are specified using <apex:column> tags.
The following Visualforce snippet is all thats needed to display the list of already entered sprints
and shouldnt pose any difficulty; this new page block goes below the one containing the input
form.
<apex:pageBlock title=Sprints>
<apex:pageBlockTable value={!sprints} var=s>
<apex:column value={!s.Name}/>
<apex:column value={!s.Start_Date__c}/>
</apex:pageBlockTable>
</apex:pageBlock>
Adding Milestones
Everything is coming together nicely; we now have the ability for the user to create a project
and easily attach multiple sprints to it as child records, so the next logical step is to work on the
third page, which will allow stories to be added to sprints. The work here will largely be a mirror
image of what we have done for the sprints already, especially for the page itself, but there are
75
Next, we modify the table to add a third column with a link to add stories to the sprint in that row.
Instead of specifying a value in the <apex:column> tag, we create a pair of tags, inside which we
place a pair of <apex:commandLink> tags, and inside those goes the <apex:param> tag. This
<apex:param> tag in turn specifies the value we want to pass to the controller (sprint ID) and
the variable to assign it to:
<apex:column headerValue=Action>
<apex:commandLink action={!ToPage3} value=Add Stories>
<apex:param value={!s.Id} name=selected
assignTo={!selectedSprint}/>
</apex:commandLink>
</apex:column>
Page 3 itself will function much like Page 2: add a list of stories as a member variable to the
controller, a story variable to capture input, and write a custom save action method to insert the
story into the database and add it to the list. This time, the value for the master-detail lookup field
will come from the selectedSprint member variable, as the user will expect the stories to be
added to the sprint they clicked. The save method will look like this:
public PageReference SaveStory()
{
story.Sprint__c = selectedSprint;
insert story;
stories.Add(story);
story = new Story__c();
}
return null;
The mark-up for the page is almost identical to the mark-up for Page 2, and should need no
explanation:
<apex:pageBlock title=Story Details>
<apex:pageBlockButtons>
<apex:commandButton action={!SaveStory} value=Save/>
76
Wizards
</apex:pageBlockButtons>
<apex:pageBlockSection>
<apex:inputField value={!story.Theme__c}/>
<apex:inputField value={!story.Points__c}/>
<apex:inputField value={!story.Story__c}/>
</apex:pageBlockSection>
</apex:pageBlock>
<apex:pageBlock title=Stories>
<apex:pageBlockTable value={!stories} var=s>
<apex:column value={!s.Theme__c}/>
<apex:column value={!s.Points__c}/>
<apex:column value={!s.Story__c}/>
</apex:pageBlockTable>
</apex:pageBlock>
One extra thing to add to this page is a button to allow the user to return to Page 2 so they can
add stories to other sprints. We already have the action method for doing this, ToPage2, but in
our data model we made the story field on the story object required, as a record without that data
is useless. The side effect of this is that if the user attempts to navigate back to Page 2 with that
field empty, theyll receive an error from the system saying that its required. Clearly, we dont
want them to have to write a value in the field just to navigate away from it, so we must use the
attribute immediate on the command button. It calls the action method on the controller without
performing any data validation or sending any data to it.
<apex:commandButton action={!ToPage2} value=Sprints
immediate=true/>
With that milestone, our project is nearing completion, but if you take the time to test adding
stories to multiple sprints, youll notice a rather glaring bug: when you click Add Stories on a
second sprint, the list of existing milestones still shows the ones from the previous story. Clearly,
this is undesirable, will only lead to confusion for the end user, and so must be avoided.
What we need to do is ensure that the list of stories is relevant to the selected sprint, and a good
time to do that is when the selected sprint changes, i.e., in the ToPage3 action method. One option
is to manage the lists for all sprints inside the controller, using a map to access a different list of
stories for each sprint ID. Another option, and the one we will use, is to ask the database for the
list of milestones related to the newly chosen sprint.
In the ToPage3 action method, we update the stories list variable, assigning it to the result of a
simple SOQL query. If youre familiar with SOQL, feel free to skip the rest of this paragraph, if not,
read on. The basic form of a SOQL query is
SELECT fields FROM object WHERE condition
where fields is a comma-separated list of field API names on the object being queried, object
is the object API name of that object, and WHERE condition is an optional part allowing you to
filter the data retrieved. Our table displays the story theme, points value, and story from the story
object, so the first part of the query is SELECT Theme__c, Points__c, Story__c FROM
Story__c. If we left the query at that, it would return all of the story records in the database,
77
The query is executed in code by putting it in square brackets ([ ]), and the result is a list of
matching records in a list.
public PageReference ToPage3()
{
stories = [SELECT Id, Theme__c, Points__c, Story__c FROM Story__c
where Sprint__c = : selectedSprint];
return Page.ProjectCreate3;
}
Now when navigating back to the sprints page and choosing a new sprint to add stories to, the list
is refreshed and always shows the correct information.
Tidying Up
Our pages are now fully functional in terms of achieving what we set out to do, though there are a
few minor changes we should consider to make life a little easier for end users.
One major sticking point at the moment is that theres no way to navigate away from our three
pages back to the standard user interface, and an easy solution to this is to add another command
button to the second page (since users will spend most of their time there) that links back to the
standard detail page for the project record. Once again, having the standard controller makes life
easier, we can simply trigger the standard save action, the default behavior of which is to update
the record in the database and return the user to the page we desire.
Another sensible idea is to let the user use the custom interface for editing old projects as well
as for adding new ones. Hooking it up to the project objects Edit button is easy enough as the
process is the same as we used before when overriding the New button. Once thats done, a new
issue becomes apparent, and that is that any existing sprints will not show up on Page 2 because
were not querying the database. When working with new projects, it isnt an issue because we are
creating the new sprint records in our controller and maintaining a list there. But now we need to
ensure that list is populated with those already in the system too.
As with the milestones, this issue can be resolved by adding a SOQL query into the navigation
action method used to navigate to Page 2, though well only need to do so when navigating
from Page 1. We already have code to handle that (which we used to save the project record), so
inside that existing if statement, we can now populate the list of sprints with information from
the database. Were going to need the projects ID to find the applicable sprints, so well move the
code that grabs the project record from the SaveSprint() method and move it into the if block.
78
Wizards
So that the sprints can still obtain the project ID, well make the project variable used a member
variable belonging to the class:
private Project__c project = null;
The SaveSprint() method should now start like this:
public PageReference SaveSprint()
{
sprint.Project__c = project.Id;
insert sprint;
And then the if statement inside ToPage2() is expanded like so:
public PageReference ToPage2()
{
if(ApexPages.CurrentPage().GetURL().ToLowerCase().StartsWith(/
apex/projectcreate1))
{
sc.Save();
project = (Project__c)sc.GetRecord();
sprints = [select Id, Name, Start_Date__c from Sprint__c
where Project__c = : project.Id];
}
return Page.ProjectCreate2;
}
Test the new functionality by clicking the Edit button while viewing a project record in the
standard UI and everything should behave as expected.
Test Coverage
Although our controller isnt particularly complicated, its going to require test coverage if its to
be deployed into a production org. Testing Visualforce pages involves writing code that will act
like an end user, using a special method called System.Assert() to ensure certain conditions
are met along the way. Should one of the assertions fail, the test will fail and well know something
isnt functioning as it should.
Test code should go into a new class using the @IsTest annotation. The initial skeleton looks
like this:
@IsTest
public class ProjectCreateTests
{
@IsTest
public static void TestControllerExtension()
{
}
79
Inside the TestControllerExtension() method, we write code that emulates a users actions,
and we have to start by specifying which page were working with. Then we create an instance of
the controller extension class, and since that relies on a standard controller, we have to create one
of those to pass to it. In turn, creating a standard controller requires a record, so well create an
empty Project__c record that will mimic the process of a user creating a new project.
Project__c project = new Project__c();
ApexPages.StandardController sc = new ApexPages.
StandardController(project);
ProjectCreateExtension pce = new ProjectCreateExtension(sc);
Next, the user will enter some information about the project and click the Save button. We
can simulate this by manually assigning some values to the project record and then calling the
ToPage2() action. By default, test methods cannot see data in the system, so well also add our
first assertion: if we query the database for all Project__c records, there should only be one of
them, the one saved when navigating to Page 2.
project.Name = Test Project;
project.Budget__c = 1000;
project.Start_Date__c = System.Today();
pce.ToPage2();
System.Assert([select Id from Project__c].Size() == 1);
If you run the tests at this point in the Developer Console by selecting Test > New Run and then
choosing the test class in the popup, a run log will appear under the Tests tab at the bottom of the
screen. Double-click that entry and expand it all the way to reveal the test class name, and then
double-click that to update the results pane on the right-hand side. ProjectCreateExtension should
be listed in there and showing as around 50 percent covereddouble-clicking that will bring up
the class with covered lines in highlighted blue and untested lines highlighted in red. Completing
the test class is left as an exercise for the reader. The steps above will give you all the tools needed
to finish the job and reach 100% code coverage.
80
Chapter 6
Building Charts With Visualforce
C
Getting Chart Data
Processing Chart Data
Chart Elements
Creating the Container
Line Charts
Bar Charts
Scatter Charts
Line Charts
Pie Charts
Guage Charts
Radar Charts
APPROACH
First well cover technical limitations and other
constraints youll need to take into consideration when
designing and implementing VisualForce charts. Then
well cover the various options for displaying your chart
within Salesforce, as youll have a number of contexts
available for embedding VisualForce charts. Next well
discuss the high-level architecture of VisualForce charts
to understand how they work, how they need to be built,
and then well get into the details you need to know to
build great charts. Finally, well reinforce these concepts
with examples and code samples.
81
CONSIDERATIONS
Before we dive into designing and building VisualForce charts, there are a number of requirements
and limitations that we need to take into consideration.
VisualForce renders charts in browsers at runtime using JavaScript. This is important to
understand, and it limits how and where you can use VisualForce charts. For example, browsers
must support Scalable Vector Graphics (SVG) in order to render VisualForce charts. VisualForce
Charts will also not display in VisualForce pages rendered as PDF. If you plan to include
VisualForce charts in email messages or email templates, youll need to make sure that the
recipients email clients support JavaScript, otherwise the charts will not render.
Because VisualForce charts use JavaScript for rendering, it bubbles up errors and messages to
the JavaScript console and not to VisualForce. This means that you wont be able to troubleshoot
rendering errors using tools such as Salesforce Debug Logs or the Developer Console. Instead,
youll need to use a debugging tool such as the built-in JavaScript debuggers in modern browsers,
such as Firefox or Chrome, or with a popular plugin such as Firebug for the Firefox browser.
82
Decide whether youre embedding the chart in a 1-Column or 2-Column section layout.
Specify the width of the embedded page as either a % of the column, or a number of pixels.
The default is 100% of the column width.
Charts
Specify the height of the embedded page in pixels. The default is 200px.
Choose whether to use scrollbars if the page size exceeds the height constraint.
Choose whether a label should appear with the name of the embedded page.
A Standard Controller
A Custom Controller
No Controller
83
Controller method
JavaScript Function
JavaScript Array
Controller Method
If you want to write a method in a Custom Controller or Controller Extension to provide Chart
data, you need to make sure your method returns data as a List of either sObjects, AggregateResult
objects, or Apex wrapper objects. If youre using a Standard Controller, you can access an sObjects
List by referencing the children of the record in context.
When you use a Controller method, you dont need to worry about serializing to JSON as this
will be handled for you server-side.
JavaScript Function
If you dont have a Controller method for the Chart to use for its data, or if you want to apply
additional client-side logic or processing, you can specify the name of a JavaScript function that
will provide the Chart with its data.
This JavaScript function must either be present on the VisualForce page or referenced by the
page, it must be constructed to accept a callback function as a parameter, and invoke that callback
with the result object generated by the function.
JavaScript Array
You can define a JavaScript array that contains JSON objects the Chart will use for its data. This is
a good option if youll be using data that comes from a source other than Salesforce, or if youll be
aggregating data from multiple sources client-side.
Using JavaScript arrays also gives you an interesting option for abstracting data into a reusable
data model that can be consumed by other functions and components, such as custom JavaScript
frameworks and components. For example, you can define an array that can be consumed by both
a VisualForce Chart and a Google Chart on the same page, or populate an ExtJS store.
Container
Every VisualForce Chart must be created in a container. Chart containers are represented by the
<apex:chart> standard component, which defines the basic attributes of a Chart, including
where the data will be coming from and the size of the container.
84
Charts
Colors
Youll see several references to colors within VisualForce Chart components. While the application
of colors is specific to the context of the individual component, color attributes are universally
defined in components using comma-separated HTML color values in hexadecimal notation. For
example, if you want to define a sequence of color values for black, red, yellow, blue, and white,
represent them as follows:
attributeName=#000000, #FF0000, #FFFF00, #0000FF, #FFFFFF
Axis
Most charts display data using axes, which represent data in multiple dimensions. The horizontal
axis is referred to as the X-axis, and the vertical axis is the Y-axis.
The <apex:axis> component is a child of the <apex:chart> container and allows you to define
attributes specific to the visual options for axis, including labels and scale.
Legend
By default, VisualForce charts include a legend, represented as a box that displays a list of labels
with their corresponding visual representation to serve as a key for the user viewing the chart.
The <apex:legend> component allows you to define attributes specific to the Legend, and
must be a child of the <apex:chart> container.
Series
The type of chart rendered is defined by its Series. The Series is a child element of the
<apex:chart> container, and is defined by specific VisualForce components and their associated
attributes:
85
animate [Boolean |
Optional]
background [String |
Optional]
colorSet [String |
Optional]
data [Object |
REQUIRED]
floating [Boolean |
Optional]
height [String |
REQUIRED]
hidden [Boolean |
Optional]
id [String | Optional]
legend [Boolean |
Optional]
name [String |
Optional]
rendered [Boolean |
Optional]
renderTo [String |
Optional]
resizable [Boolean |
Optional]
theme [String |
Optional]
width [String |
REQUIRED]
86
Description
Do you want your chart to render with a simple animation?
You cannot specify any attributes for the animation itself, only
whether it animates on render. The default value is True.
Controls the background color of the chart using HTML
hexadecimal color values. The default value is #FFFFFF (a plain
white background).
Specifies the colors assigned to each series used by charts
in this container and overrides their default values, using a
comma-separated set of HTML hexadecimal color values. If
an individual data series within this container also includes a
colorSet attribute, it overrides the values specified here.
Specifies the data used by the underlying chart. See Getting
Chart Data above for options.
Specifies whether to use CSS absolute positioning to float the
chart outside the normal bounds of the HTML document.
Defines the height of the container, specified as either an integer
for a number of pixels, or as a percentage of the height of the
HTML element that contains this chart.
Specifies whether the chart is hidden when the page loads.
Specifies a unique identifier that allows this chart to be
referenced by other VisualForce components on the same page.
Specifies whether the default chart legend should be displayed.
Using <apex:legend> allows you to specify additional Legend
attributes. The default value is True.
Generates a name for the JavaScript object that gets created at
runtime. This name can be used by other JavaScript functions
and inherits any namespace defined by the parent VisualForce
Page or Component, and must be unique across all VisualForce
Chart components.
Specifies whether the Chart is rendered on the page. The default
value is True.
Specifies whether or not to render the Chart to a specific DOM
element present on the page.
Specifies whether or not the Chart can be resized.
Specifies the name of the theme that defines sets of colors to be
applied to your Chart series. Do not specify a theme if youre
defining Chart colors using the colorSet attribute.
Defines the width of the container, specified as either an integer
for a number of pixels, or as a percentage of the height of the
HTML element that contains this chart.
Charts
Bar Charts
A bar chart represents data as rectangular-shaped bars that can be plotted horizontally or vertically. A bar chart is a good choice for representing values for comparison, with data being described
as the intersection of X and Y coordinates. Bar charts will have one axis that represents the subject
being compared and an axis that represents specific data values.
For more complex comparisons, bar charts can be stacked or grouped to provide additional
context. Stacked bar charts show how multiple subcategories can be combined to create a single
aggregated bar, with each subelement assigned its own color or pattern within the bar. Grouped
bar charts show two or more bars within each category, with each bar being assigned its own color
or pattern.
87
Description
axis [String | REQUIRED]
Specifies the axis that the barSeries will bind to, as defined
in an <apex:axis> component. Values include: top, bottom,
left, right.
colorSet [String |
Specifies the bar fill colors using a comma-separated set of
Optional]
HTML hexadecimal color values.
colorsProgressWithinSeries Use True to cycle through your colorSet and apply a
[Boolean | Optional]
different color to each bar or bar segment in this barSeries,
with colors restarting from the beginning after using all
colors in the colorSet. Use false if you want to have all bars
in a barSeries use the same color from a colorSet, and then
cycle through the remaining colors in the colorSet for each
additional barSeries defined in this Chart.
groupGutter [Integer |
Specifies the percentage of the bar width to apply to the
Optional]
spacing between groups of bars.
gutter [Integer | Optional]
Specifies the percentage of the bar width to apply to the
spacing between the individual bars.
highlight [Boolean |
Specifies whether or not a bar is highlighted when you
Optional]
hover the mouse over it. The default value is True.
highlightColor [String |
If you have specified a value for highlight, use this
Optional]
attribute to specify the HTML hexadecimal color value of
the highlighted bar.
highlightLineWidth
Defines the width of the line that borders the highlighted
[Integer | Optional]
Bar, specified as an integer for the number of pixels.
highlightOpacity [String | Defines the amount of opacity applied to the color of
Optional]
the highlighted Bar. This is specified as a decimal value
from 0.0 - 1.0, with lower values making the color more
transparent.
highlightStroke [String |
Specifies the color of the line that borders the highlighted
Optional]
Bar using HTML hexadecimal values.
id [String | Optional]
Specifies a unique identifier that allows this barSeries to be
referenced by other VisualForce components on the same
page.
orientation [String |
Specifies whether you want the Bars in your Chart to be
REQUIRED]
vertical or horizontal. The default value is vertical.
rendered [Boolean |
Specifies whether or not the Series is rendered on the
Optional]
page. The default value is True.
88
Charts
<apex:barSeries> Attribute
Description
If you want to use a custom JavaScript function to apply
additional formatting or process data, specify the name of
the function as a String.
showInLegend [Boolean |
Specifies whether or not the Series should be included in
Optional]
the Legend for the Chart. The default value is True.
stacked [Boolean |
If True, the Bars in this barSeries are stacked. If False, the
Optional]
Bars are grouped.
tips [Boolean | Optional]
Specifies whether or not a tool tip in the format of xField:
yField displays when the mouse hovers over a Bar. The
default value is True.
title [String | Optional]
Specifies the title of the Series to be used in the Legend.
If you are using multiple Series in a stacked Chart, specify
each name separated by a comma in Title1, Title2, Title3
format.
xField [String | REQUIRED] Specifies a required attribute that determines the field in
the Chart data object containing the values for each data
point on the X-axis.
xPadding [Integer |
Specifies the amount of padding that should appear
Optional]
between Bars and the left and right axes.
yField [String | REQUIRED] Specifies a required attribute that determines the field in
the Chart data object containing the values for each data
point on the Y-axis.
yPadding [Integer |
Specifies the amount of padding that should appear
Optional]
between Bars and the top and bottom axes.
rendererFn [String |
Optional]
Line Charts
Typically used to visualize data trends over a period of time, line charts represent data as a series
of points connected by line segments.
Like Bar Charts, Line Charts are two-dimensional, with a horizontal X-axis and a vertical Y-axis.
To aid in visualization, line charts can have parallel lines drawn that extend from either axis; you
can create a grid by extending lines from both the X-axis and Y-axis.
Line Charts can include multiple series, allowing you to compare multiple data points. Line
Charts can also be combined with Bar Charts to create a visual overlay that displays multiple
related data series.
89
fillColor [String |
Optional]
highlight [Boolean |
Optional]
highlightStrokeWidth
[String | Optional]
id [String | Optional]
markerFill [String |
Optional]
markerSize [Integer |
Optional]
markerType [String |
Optional]
opacity [String |
Optional]
rendered [Boolean |
Optional]
rendererFn [String |
Optional]
showInLegend [Boolean |
Optional]
90
Description
Specifies the axis the lineSeries will bind to, as defined in
an <apex:axis> component. Values include: top, bottom,
left, right.
Specifies whether you want to fill the area between the
line and the axis with a color. You can also optionally
specify the color of the fill using the fillColor attribute,
otherwise the fill color defaults to the same color as the
line. The default value is false.
If you have chosen to use a fill, you can specify its HTML
hexadecimal color value.
Specifies whether or not points on the lineSeries are
highlighted when you hover the mouse over them. The
default value is True.
Defines the width of the line that appears over the
highlighted line in the lineSeries, specified as String.
Specifies a unique identifier that allows this lineSeries to
be referenced by other VisualForce components on the
same page.
Specifies the HTML hexadecimal color value used for the
data point markers in this lineSeries. If this attribute is not
defined, the color defaults to the same color of the line.
Specifies the size of the data point markers in this
lineSeries. If this attribute is not defined, the size defaults
to 3.
You can specify whether the data point marker will be a
circle or a cross. If you dont define this attribute, the
data point marker shape defaults to the next shape in the
sequence.
Defines the amount of opacity applied to the filled area
that appears beneath the line. Specified as a decimal value
from 0.0 - 1.0, with lower values making the color more
transparent.
Specifies whether or not this lineSeries is rendered on the
page. The default value is True.
If you want to use a custom JavaScript function to apply
additional formatting or process data, specify the name of
the function as a String.
Specifies whether or not the lineSeries should be included
in the Legend for the Chart. The default value is True.
Charts
<apex:lineSeries> Attribute
smooth [Integer |
Optional]
strokeColor [String |
Optional]
strokeWidth [String |
Optional]
tips [Boolean | Optional]
Description
Specifies the amount of smoothing applied to the line.
A lower number indicates more smoothing is applied.
Use a value of 0 to disable smoothing and simply show a
straight line between data points.
Specifies the HTML hexadecimal color value used for the
line in this lineSeries.
Defines the width of the line, specified as an integer.
Area Charts
Area Charts are fundamentally similar to Line Charts, with the exception being that the area
between the axis and the line are filled with colors or patterns. Area charts are great for visualizing
trends of related categories over time, usually representing aggregated totals or percentages.
91
<apex:areaSeries> Attributes
Description
Specifies the axis that the areaSeries will bind to, as
defined in an <apex:axis> component. Values include:
top, bottom, left, right.
colorSet [String |
Specifies the bar fill colors using a comma-separated set of
Optional]
HTML hexadecimal color values.
highlight [Boolean |
Specifies whether or not an area is highlighted when you
Optional]
hover the mouse over it. The default value is True.
highlightLineWidth
Defines the width of the line that surrounds the
[Integer | Optional]
highlighted area, specified as an integer for the number of
pixels.
highlightOpacity [String | Defines the amount of opacity applied to the color of the
Optional]
highlighted area. Specified as a decimal value from 0.0 1.0, with lower values making the color more transparent.
axis [String | REQUIRED]
92
Charts
<apex:areaSeries> Attributes
Description
If you have specified a value for highlight, use this
attribute to specify the HTML hexadecimal color value of
the highlighted area.
id [String | Optional]
Specifies a unique identifier that allows this areaSeries to
be referenced by other VisualForce components on the
same page.
opacity [String |
Defines the amount of opacity applied to the filled area.
Optional]
Specified as a decimal value from 0.0 - 1.0, with lower
values making the color more transparent.
rendered [Boolean |
Specifies whether or not this areaSeries is rendered on the
Optional]
page. The default value is True.
rendererFn [String |
If you want to use a custom JavaScript function to apply
Optional]
additional formatting or process data, specify the name of
the function as a String.
showInLegend [Boolean |
Specifies whether or not this areaSeries should be included
Optional]
in the legend for the chart. The default value is True.
tips [Boolean | Optional]
Specifies whether or not a tool tip in the format of xField:
yField displays when the mouse hovers over a data point.
The default value is True.
title [String | Optional]
Specifies the title of the areaSeries to be used in the legend.
If youre using multiple series in a stacked chart, specify
each name separated by a comma in Title1, Title2, Title3
format.
xField [String | REQUIRED] Specifies a required attribute that determines the field in
the Chart data object containing the values for each data
point on the X-axis.
yField [String | REQUIRED] Specifies a required attribute that determines the field in
the Chart data object containing the values for each data
point on the Y-axis.
highlightStrokeColor
[String | Optional]
Scatter Charts
Scatter Charts and Line Charts might look similar at first blush, but there are fundamental differences between the two. A Line Chart represents data as evenly distributed values along the X-axis,
while a Scatter Chart uses two axes to display data values: one along the Y-axis and one along the
X-axis. Unlike a Line Chart, values in a Scatter Chart are represented individually and are not
connected.
Line Charts tend to be used to show data sequentially, whereas Scatter charts are used to
visualize larger, grouped data sets to find correlations or patterns in the data. Scatter charts allow
you to compare data without constraining to a sequence or time period.
93
Description
Specifies the axis that the scatterSeries will bind to, as
defined in an <apex:axis> component. Values include: top,
bottom, left, right.
highlight [Boolean |
Specifies whether or not a data point is highlighted when
Optional]
you hover the mouse over it. The default value is True.
id [String | Optional]
Specifies a unique identifier that allows this scatterSeries
to be referenced by other VisualForce components on the
same page.
markerFill [String |
Specifies the HTML hexadecimal color value used for the
Optional]
data point markers in this scatterSeries.
markerSize [Integer |
Specifies the size of the data point markers in this
Optional]
scatterSeries.
markerType [String |
You can specify whether the data point marker will be a
Optional]
circle or a cross. If you do not define this attribute, the
data point marker shape defaults to the next shape in the
sequence.
rendered [Boolean |
Specifies whether or not this scatterSeries is rendered on
Optional]
the page. The default value is True.
rendererFn [String |
If you want to use a custom JavaScript function to apply
Optional]
additional formatting or process data, specify the name of
the function as a String.
showInLegend [Boolean |
Specifies whether or not this scatterSeries should be
Optional]
included in the legend for the chart. The default value is
True.
tips [Boolean | Optional]
Specifies whether or not a tool tip in the format of xField:
yField displays when the mouse hovers over a data point.
The default value is True.
title [String | Optional]
Specifies the title of the scatterSeries to be used in the
legend.
xField [String | REQUIRED] Specifies a required attribute that determines the field in
the Chart data object containing the values for each data
point on the X-axis.
yField [String | REQUIRED] Specifies a required attribute that determines the field in
the Chart data object containing the values for each data
point on the Y-axis.
axis [String | REQUIRED]
94
Charts
Pie Charts
Pie charts are probably the most commonly used chart type in business. Pie charts represent a
data set in the shape of a full circle. Think of a fresh-baked apple pie with individual data values
represented as slices of the pie, referred to as wedges, sized proportionately relative to the value
compared to the whole.
Pie charts are great for representing a simple data set that compares a limited number of
categories or values. Pie charts are not good for representing values over time, and they tend to
become unruly when too many categories are represented on a single chart.
<apex:pieSeries> Attributes
colorSet [String |
Optional]
Description
Specifies the fill colors of each wedge in sequence using a
comma-separated set of HTML hexadecimal color values.
dataField [String |
REQUIRED]
95
id [String | Optional]
labelField [String |
Optional]
rendered [Boolean |
Optional]
rendererFn [String |
Optional]
showInLegend [Boolean |
Optional]
tips [Boolean | Optional]
Description
Specifies whether or not each pie wedge is highlighted
when you hover the mouse over it. The default value is
True.
Specifies a unique identifier that allows this chart to be
referenced by other VisualForce components on the same
page.
Specify the name of the field that appears in each data
record that contains the label for each pie wedge. If you
dont specify a value for this attribute, each pie wedge
receives a generic label of name.
Specifies whether this pieSeries is rendered on the page.
The default value is True.
If you want to use a custom JavaScript function to apply
additional formatting or process data, specify the name of
the function as a String.
Specifies whether or not this pieSeries should be included
in the legend for the chart. The default value is True.
Specifies whether or not a tool tip in the format of
labelField: dataField displays when the mouse hovers
over a pie wedge. The default value is True.
Gauge Charts
Gauge charts make for good dashboard components, as they are shaped like an instrument you
typically find on your cars dashboard to measure things like speed, RPM, etc. A Gauge Charts
look like a donut cut in half.
Use Gauge Charts to measure progress against a stated goal. Shade different regions of the chart
to show markers/progress against the goal. For example, if you are a salesperson with a quota, you
can track your closed revenue against your quota goal with a gauge chart.
96
Charts
<apex:gaugeSeries> Attributes
colorSet [String | Optional]
dataField [String | REQUIRED]
id [String | Optional]
Description
Specifies the gauge fill colors using a comma-separated set
of HTML hexadecimal color values.
Specify the name of the field that appears in each data
record that will contain the data value for the gauge level.
Note that only data from the first record is used.
Specify the size of the hole that appears in the center of
your Gauge Chart as an integer value from 0-100. This
number represents the percentage of the radius of the
gauge, so a value of 0 means no hole appears, and a value
of 100 means the hole consumes the entire gauge.
Specifies whether or not each gauge level is highlighted
when you hover the mouse over it. The default value is
True.
Specifies a unique identifier that allows this
gaugeSeries to be referenced by other VisualForce
components on the same page.
Specify the name of the field that appears in each data
record containing the label for the gauge level. If you dont
specify a value for this attribute, a generic label of name
is assigned. Similar to the dataField attribute, only the
label from the first record is used.
Set this value to True if you want a needle to appear on the
gauge chart. The default value is False.
Specifies whether or not this gaugeSeries is rendered on
the page. The default value is True.
If you want to use a custom JavaScript function to apply
additional formatting or process data, specify the name of
the function as a String.
Specifies whether or not a tool tip in the format of
labelField: dataField displays when the mouse hovers
over a gauge level. The default value is True.
97
Radar Charts
Radar Charts might look complex on the surface, but at the heart they are basically Line Charts
wrapped into a circular axis. You might have heard radar charts referred to as spider charts because of the resemblance to a spider web.
Radar Charts plot values for each category along an axis that starts in the center of the chart and
extends to the outer edge of the outermost ring of the chart. They are commonly used to show
similarities across different categories in a data set and to help identify outliers.
<apex:radarSeries> Attributes
highlight [Boolean |
Optional]
id [String | Optional]
markerFill [String |
Optional]
markerSize [Integer |
Optional]
markerType [String |
Optional]
opacity [String |
Optional]
rendered [Boolean |
Optional]
showInLegend [Boolean |
Optional]
strokeColor [String |
Optional]
98
Description
If you want to fill the area inside of a line, use an HTML
hexadecimal value for your desired color. If you dont
specify a color, the next color in the sequence for the
inherited colorSet or theme is used. If you specify
none for the value, no fill is applied. In this case, youll
need to specify values for marker and stroke attributes, as
youll only see lines and markers, and they are not visible
by default.
Specifies whether or not a data point is highlighted when
you hover the mouse over it. The default value is True.
Specifies a unique identifier that allows this radarSeries
to be referenced by other VisualForce components on the
same page.
Specifies the HTML hexadecimal color value used for the
data point markers in this.
Specifies the size of the data point markers in this
radarSeries.
You can specify whether the data point marker will be a
circle or a cross. If you dont define this attribute, the
data point marker shape defaults to the next shape in the
sequence.
Defines the amount of opacity applied to the filled area.
Specified as a decimal value from 0.0 - 1.0, with lower
values making the color more transparent.
Specifies whether or not this radarSeries is rendered
on the page. The default value is True.
Specifies whether or not this radarSeries should be
included in the legend for the chart. The default value is
True.
Specifies the HTML hexadecimal color value used for the
line in this series.
Charts
<apex:radarSeries> Attributes
strokeWidth [String |
Optional]
tips [Boolean | Optional]
Description
Defines the width of the line, specified as an integer.
Summary
In this chapter we learned about the different options for creating custom charts in VisualForce,
including the chart types available and how to configure each type to meet the requirements of
your business users. You should have a better understanding of how to get data for your chart and
process it for rendering, as well as where VisualForce Charts can be used in Salesforce.
Now get charting!
99
100
Chapter 7
Building Dashboards with Visualforce
Y
Use Cases for Dashboards
Realtime Updating Charts
Rotating Content
Tables, Lists and Grids
JavaScript Widgets
Building Visualforce
Dashboard Components
Adding Pages to
Dashboards
ExampleCreating a Sales
Leaderboard Component
Considerations
Adding a Visualforce page to a Salesforce Dashboard
is not a difficult undertaking, but there are some key
technical requirements and limitations that youll need to
understand.
Because screen real estate is constrained on a
dashboard, youll need to design your custom Visualforce
101
Charts
Since the predominant standard component types used in dashboards are charts representing
underlying reports as data sources, its only fitting that charts are also a common design choice for
custom Visualforce dashboard components.
In the previous chapter we learned how to create Bar Charts, Line Charts, Area Charts, Scatter
Charts, Pie Charts, Gauge Charts, and Radar Charts. As long as the Visualforce pages containing
your Visualforce charts adhere to controller restrictions, you can use them as custom Visualforce
dashboard components.
Because standard Salesforce dashboard components offer a wide selection of chart types to
choose from, youll want to create custom dashboard components for charts only if its not possible
to use a standard component due to data limitations, or if the chart types or styling that your
business users require can only be created using a custom chart component.
Visualforce Charts are not your only option for creating and displaying charts. Google Charts
provides many data visualization options to choose from for creating charts for custom dashboard
components, as do a number of JavaScript frameworks and third-party charting packages. One
thing to keep in mind if you decide to leverage a third party for generating charts is that if the tool
uses Adobe Flex to render the visualizations, many tablets and mobile devices will not be able to
display them due to limited support for Adobe Flash on these devices.
102
Building Dashboards
A significant limitation of Salesforce dashboards is that they do not refresh in real time.
Dashboards are either refreshed manually by clicking a button, or on a daily, weekly, or monthly
schedule. For a Salesforce dashboard to display data thats refreshed more frequently, youll need to
create a custom Visualforce dashboard component.
An emerging design pattern is to use a JavaScript library that supports rich, animated content
that leverages AJAX for frequent data refreshes. The ExtJS framework from Sencha is a great
example of a JavaScript framework that contains a wide array of beautiful charts and other
visualizations that support animation with either scheduled or real-time data refreshing. See
Chapter 10 for more information on using JavaScript with Visualforce.
Rotating Content
If your organizations content refreshes often, such as announcements or press releases, you can
create a Visualforce page to display the content in a form factor tailored for a Salesforce dashboard.
The content can be refreshed at any time, because its not subject to the refresh frequency
limitations of standard Dashboard components.
Tailored Content
Even though custom Visualforce dashboard components are limited to Visualforce pages that
adhere to controller limitations for custom components, you still get access to a wealth of
contextual information with Visualforce global variables and Apex methods.
With this contextual information, you can tailor the content displayed in a custom Visualforce
dashboard component to the logged-in user based on criteria such as their assigned Role or
Profile, membership in a Chatter Group or Public Group, or even the time of day.
Interactive Content
Dashboards do not need to be read only. If your business users require a component that
allows for any level of real-time interaction, youll need to use a custom Visualforce dashboard
component.
For example, if your company wants to conduct employee surveys from within Salesforce, you
can design the survey as a Visualforce page and embed it as a custom dashboard component in a
dashboard that every employee sees on their Home tab when they log in to Salesforce.
103
Data Mashups
If your organization is like most organizations, you likely have systems in place other than
Salesforce that contain company data. While standard dashboard components are dependent
on underlying reports for their source data, and those reports are limited to data that resides in
Salesforce, custom Visualforce dashboard components are not subject to the same limitation.
Sometimes your business users want to see data thats aggregated from multiple source systems
or services. If spending hours pulling the data together in Excel spreadsheets or making an
investment in a business intelligence (BI) tool are not attractive options, a custom Visualforce
dashboard component might be the ideal solution.
JavaScript Widgets
Search the web and you can find templates and toolkits for consuming data from a virtually
unlimited number of services using JavaScript and AJAX. Many services also provide pre-built
widgets for interacting with the service.
Bound only by your imagination, you can use JavaScript widgets in Visualforce pages used as
dashboard components to add contextually relevant data to your Salesforce dashboards. Some
examples include:
Twitter
Facebook
News Feed
Currency Converter
Weather
Stock Ticker
104
Use the Twitter Widget to embed your company Twitter feed right in
a Salesforce dashboard.
Use a Facebook Plugin to view activity on your company Facebook
page.
Aggregate relevant news about your organization, customer, or
industry in a single view that can be displayed on the Home tab
when Salesforce users log in.
Give your business users access to financial tools, such as a currency
converter in a dashboard.
Embed a weather widget in a dashboard used on the Home tab and
you can display todays weather based on the logged-in users zip
code or city and state.
Have your companys stock or major indexes tracked in a dashboard
component.
Building Dashboards
Enter the text you want to display in the header of this component.
Enter the text you want to display in the footer of this component.
If you want to remove the Visualforce Page data source, click the X that
appears on the right side of the selected component.
If you click the wrench icon for Edit Attributes on the component, youll
be given the option to set the height of the container in pixels. You can
either leave this value blank and accept the default height for the container,
or specify a value from 1-1000 pixels.
Medium
Wide
Dashboards in Practice
Creating a Sales Leaderboard Dashboard Component
We have learned about incorporating Visualforce pages into dashboards as custom components,
but what does this look like in practice?
As mentioned previously, sometimes a simple table can go a long way in providing relevant, realtime data to your business users when they are viewing their Home tab in Salesforce or viewing
one of their dashboards.
For this example, we are going to create a Sales Leaderboard that shows the top five salespeople
by Closed Won opportunities in the current year. The Leaderboard will display the name of the
salesperson, the number of Closed Won opportunities, and the amount of revenue closed so far
this year and to give it a little bit of a wow factor, were also going to include the Chatter profile
picture for each of the top salespeople.
106
Building Dashboards
107
Next, well create a Map that will contain the User data well match up with the AggregateResult
objects in our List. Since we have OwnerId as a value we can use to combine Opportunity data
with User data, well use the User ID for our Map keys, and well use the User sObject for our Map
values. Since we want to display the name and small Chatter profile image for each salesperson
in our Sales Leaderboard, well retrieve the Name and SmallPhotoUrl attributes from the User
object.
NOTE: the SmallPhotoUrl field will only be available if you have enabled Chatter in your Salesforce org.
public Map<Id, User> salesUsers {
get {
if(salesUsers == null) {
salesUsers = new Map<Id, User>();
108
Building Dashboards
for(User u : [SELECT SmallPhotoUrl, Name, Id From User
WHERE Id IN :ownerIds]) {
salesUsers.put(u.id, u);
}
}
return salesUsers;
}
set;
}
Finally, well create a Get method to grab the data we need from each object in the List<
AggregateResult> and match it up with the corresponding User fields from the sObject value that
corresponds to the ID key in our Map<Id, User>. Well construct new objects and cast them to a new
List that our Visualforce PageBlockTable will consume when rendering the Sales Leaderboard.
109
for(AggregateResult ar : aggregateOpps) {
leaders.add(new leaderData(salesUsers.get(String.
ValueOf(ar.get(OwnerId))).Name, salesUsers.get(String.ValueOf(ar.
get(OwnerId))).SmallPhotoUrl, Integer.ValueOf(ar.get(cnt)),
Double.ValueOf(ar.get(amt))));
}
}
}
return leaders;
110
Building Dashboards
<apex:image url={!l.leaderPic} />
</apex:column>
<apex:column value={!l.leaderName}
headerValue=Name/>
<apex:column value={!l.oppCount} headerValue=Won/>
<apex:column headerValue=Revenue>
<apex:outputText value={0, number, 0,000}>
<apex:param value={!l.oppAmount} />
</apex:outputText>
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:page>
You can now view your page to test it and make sure it looks and feels the way you want it to by
going to the URL for the Visualforce page:
https://{Instance_name}.salesforce.com/apex/{Visualforce page name}
111
Figure 7-2. Dropping the component into position and editing the Header of the component.
Next, drag the Visualforce page from the Visualforce Pages menu under the Data Sources tab
and drop it on the component. as shown in Figure 7-3.
Save the dashboard. How does the preview look? If it needs any tuning, click the wrench icon
and set the component height if you need more real estate. To display the full Sales Leaderboard
with five salespeople and their small Chatter profile pictures, a height of 420px is optimal.
If you want the Sales Leaderboard component to display on a dashboard that you have assigned
to the Home tab, youll need to make sure its positioned at the top of one of the three columns in a
dashboard. See Figure 7-4.
Close the dashboard editor. Your Sales Leaderboard component is now live and should look
similar to Figure 7-5. Nice work!
112
Building Dashboards
Figure 7-3. Dragging the Visualforce page and dropping it onto the component.
113
Summary
In this chapter we learned about how to incorporate Visualforce pages as custom dashboard
components to give your business users access to information that goes far beyond the limitations
of standard dashboard components. Common and emerging design patterns were discussed,
and we learned about the limitations and configuration options available for custom Visualforce
dashboard components. You should feel comfortable adding Visualforce pages to dashboards as
components, as well as have enough information to troubleshoot any issues that might emerge
during the design and implementation of your custom Visualforce dashboard components.
114
Chapter 8
Streaming Realtime Data into Visualforce
T
About the Streaming API
Setup and Configuration
Creating a Push Topic
Using RemoteActions
About the Streaming API
115
In this example, only DML actions on records with the isClosed flag set to false will provide notifications
to the channel. While the query attribute of a Push Topic is SOQL, you cannot use every feature of SOQLs
syntax. Most notably, Push Topic queries do not support relationships. Adding account.name to the
query defined above will result in a failure while attempting to create or update a Push Topic. For a full list
of supported Push Topic queries, check the salesforce.com Streaming API documentation.
There are two more important attributes of a Push Topic. NotifyForFields is an enum that
is defined as All, Referenced, Select, or Where. If All is selected, a notification will be sent
regardless of which fields are updated on the record. However, the notification message will
only contain the fields defined in the query. The default value is Referenced; it will only issue
notifications if a field that is referenced in the SOQL query has changed. Similarly, the Select and
Where options generate notifications only if a field value has been updated in the SELECT or
WHERE clauses, respectively. NotifyForOperations is another enum that is defined as All, Create,
or Update. The default selection is All and generates notifications for both Create and Update
actions in the database for records that are found by the Push Topic query. The Create and Update
options limit notifications to their respective DML events.
It is important to remember that notifications delivered within channels on the Streaming API
occur only upon DML actions on records in the database. Passive changes on fields via formulas,
lookups being cleared, etc., will not initiate a notification. However, time-based workflows can
initiate a DML event, and can therefore create channel notifications where applicable. Time-based
workflow can be helpful in initiating proactive notifications from the system. An example can be
a time-based workflow that checks a box or sets a date field based on business rules. When the
workflow fires, the record is updated with a field update from a workflow action. The DML event
can initiate a Push Topic notification that is filtering based on the field being updated from the
time-based workflow action. The final result is a declarative mechanism for delivering notifications
to subscribers without hands-on interaction from a user or scheduled jobs.
116
Streaming API
In order to negotiate access to the notification channels in Visualforce pages, we need to add a few
static resources from CometD. The CometD compressed archive can be downloaded at:
http://download.cometd.org/cometd-2.2.0-distribution.tar.gz
Once you have a copy of this archive, youll need to extract it to a directory that can be easily
accessed. Well be uploading some of the files from this extract to your organizations static
resources. Once the archive is extracted, well need to take another step to extract the cometd.js file
from the cometd-javascript-common-2.2.0.war web application archive. In a Mac environment,
this can be achieved by opening a terminal in the extracted directory at:
[Your Path]/cometd-2.2.0/cometd-javascript/common/target
This will place the cometd.js file in the same directory listed above. Once this process is complete,
navigate to your static resources in Salesforce by clicking:
[Your Name]>Setup>Develop>Static Resources
Create the following static resources from your extracted CometD archive.
Static Resource Name
cometd
jquery
json2
jquery_cometd
Your environment is now set up to subscribe to channels and accept notifications from the
Streaming API.
117
Now that your Push Topic has been created, we need to test that its working. There are a variety of
ways to test, but the simplest way is to log into the Salesforce workbench at:
https://workbench.developerforce.com/login.php.
This is a tremendous tool for working with the Streaming API. It provides a reliable way to
interact with the various channels by browsing and subscribing to Push Topics. It can also provide
a sanity check if you ever lose your connection in a page and need to see if messages are being
delivered in your channel. Once you have logged into the workbench, click: Queries > Streaming
Push Topics
118
Streaming API
119
Figure 8-3.
Lets begin by creating a new Visualforce page named, SimpleCaseNotifications. Add the
following attributes to the opening page tag:
Attribute Name
id
tabstyle
Value
page
case
Next, we need to add the static resources to the page with the following tags:
<apex:includeScript value={!$Resource.cometd}/>
<apex:includeScript value={!$Resource.jquery}/>
<apex:includeScript value={!$Resource.json2}/>
<apex:includeScript value={!$Resource.jquery_cometd}/>
These IncludeScript tags load the JavaScript libraries from the static resources into the page, and
will be used to manage the subscription to the channel and ongoing long-polling. We need to add
one more block of JavaScript to the page, directly under the IncludeScript tags. This JavaScript
is used to initiate the connection to the Streaming API, and subscribe to the CaseNotifications
channel.
<script type=text/javascript>
120
var j$ = jQuery.noConflict();
j$(document).ready(function() {
Streaming API
j$.cometd.init({
url: window.location.protocol+//+window.location.
hostname+/cometd/28.0/,
requestHeaders: { Authorization: OAuth {!$Api.
Session_ID}}
});
j$.cometd.subscribe(/topic/CaseNotifications,
function(message) {
document.getElementById({!$Component.page.
block.casenumber}).innerText = message.data.sobject.CaseNumber;
document.getElementById({!$Component.page.
block.casestatus}).innerText = message.data.sobject.Status;
document.getElementById({!$Component.page.
block.casepriority}).innerText = message.data.sobject.Priority;
console.log(message);
});
});
</script>
Finally, add the body of the page as a simple page block and panel grid with text details. When the
notifications are received in the channel, the JavaScript will update the elements in the DOM with
the details. As additional notifications are received, these attributes will be overwritten to show the
latest notification details.
<apex:sectionHeader title=Simple subTitle=Case Notifications/>
<apex:pageBlock id=block>
<apex:panelGrid columns=2>
<apex:outputLabel value=Case Number: for=casenumber
style=font-weight: bold;/>
<apex:outputText value= id=casenumber/>
<apex:outputLabel value=Case Status:
for=casestatus style=font-weight: bold;/>
<apex:outputText value= id=casestatus/>
<apex:outputLabel value=Case Priority:
for=casepriority style=font-weight: bold;/>
<apex:outputText value= id=casepriority/>
</apex:panelGrid>
</apex:pageBlock>
Save your changes to the page. Your final markup should look like this:
<apex:page id=page tabStyle=Case>
<apex:includeScript
<apex:includeScript
<apex:includeScript
<apex:includeScript
value={!$Resource.cometd}/>
value={!$Resource.jquery}/>
value={!$Resource.json2}/>
value={!$Resource.jquery_cometd}/>
121
<script type=text/javascript>
var j$ = jQuery.noConflict();
j$(document).ready(function() {
j$.cometd.init({
url: window.location.protocol+//+window.location.
hostname+/cometd/28.0/,
requestHeaders: { Authorization: OAuth {!$Api.
Session_ID}}
});
j$.cometd.subscribe(/topic/CaseNotifications,
function(message) {
document.getElementById({!$Component.page.
block.casenumber}).innerText = message.data.sobject.CaseNumber;
document.getElementById({!$Component.page.
block.casestatus}).innerText = message.data.sobject.Status;
document.getElementById({!$Component.page.
block.casepriority}).innerText = message.data.sobject.Priority;
console.log(message);
});
});
</script>
<apex:sectionHeader title=Simple subTitle=Case Notifications/>
<apex:pageBlock id=block>
<apex:panelGrid columns=2>
<apex:outputLabel value=Case Number:
for=casenumber style=font-weight: bold;/>
<apex:outputText value= id=casenumber/>
<apex:outputLabel value=Case Status:
for=casestatus style=font-weight: bold;/>
<apex:outputText value= id=casestatus/>
<apex:outputLabel value=Case Priority:
for=casepriority style=font-weight: bold;/>
<apex:outputText value= id=casepriority/>
</apex:panelGrid>
</apex:pageBlock>
</apex:page>
Navigate to your new page and verify that it loads without content in the PageBlock. Open another
tab or browser and update the status or priority fields on a Case record, similar to how you tested
using the workbench. You should see the page update with the notification details. You can also
check for the same notification in the Workbench to explore additional details.
122
Streaming API
This simple page can also be dropped into a dashboard component to show a near real-time feed
of updates to the user without requiring a screen refresh, or dashboard refresh.
This example is intentionally very simple. The goal is to demonstrate how a page can subscribe
to a Push Topic channel, receive notifications, and take action to display the data to the user. This
is achieved without the use of an Apex controller, and relies on client-side JavaScript to update
elements in the DOM with the message details.
123
124
Streaming API
public with sharing class RemoteActionCaseNotificationsController {
@RemoteAction
public static Case returnCase(string caseId){
return [SELECT Id, caseNumber, status, priority,
owner.name, account.name, contact.name
FROM Case
WHERE Id = :caseId];
}
}
This query can be expanded to pull additional reference fields, as well as records in child
relationships. Save your changes to this class.
Create a new Visualforce page named RemoteActionCaseNotifications, with the same page tag
attributes as the first example. Add the same <apex:includeScript> tags as well. The body of the
page should resemble the first example; except we need to add additional <apex:outputLabel> and
<apex:outputText> tags for the additional reference fields, as shown below.
<apex:sectionHeader title=Remote Action subTitle=Case
Notifications/>
<apex:pageBlock id=block>
<apex:panelGrid columns=2>
<apex:outputLabel value=Case Number:
for=casenumber style=font-weight: bold;/>
<apex:outputText value= id=casenumber/>
<apex:outputLabel value=Case Status:
for=casestatus style=font-weight: bold;/>
<apex:outputText value= id=casestatus/>
<apex:outputLabel value=Case Priority:
for=casepriority style=font-weight: bold;/>
<apex:outputText value= id=casepriority/>
<apex:outputLabel value=Case Owner:
for=caseowner style=font-weight: bold;/>
<apex:outputText value= id=caseowner/>
<apex:outputLabel value=Account Name:
for=accountname style=font-weight: bold;/>
<apex:outputText value= id=accountname/>
<apex:outputLabel value=Contact Name:
for=contactname style=font-weight: bold;/>
<apex:outputText value= id=contactname/>
</apex:panelGrid>
</apex:pageBlock>
<apex:outputPanel layout=block id=responseErrors></
apex:outputPanel>
Following the first example, add a new JavaScript block, with the ready, init, and subscribe
functions. However, leave the content of the subscribe method empty.
125
<script type=text/javascript>
var j$ = jQuery.noConflict();
j$(document).ready(function() {
j$.cometd.init({
url: window.location.protocol+//+window.location.hostname+/
cometd/28.0/,
requestHeaders: { Authorization: OAuth {!$Api.
Session_ID}}
});
j$.cometd.subscribe(/topic/CaseNotifications,
function(message) {
});
})
</script>
Now create another Javascript function named getRemoteCase, with a signature that will accept
the ID of the record received in the channel notification. The contents of this method should
invoke the RemoteAction method from the controller, and update the elements in the DOM for
the user to review. Finally, add a call to the getRemoteCase method from within the subscribe
method. Your final JavaScript code should look like:
<script type=text/javascript>
var j$ = jQuery.noConflict();
j$(document).ready(function() {
j$.cometd.init({
url: window.location.protocol+//+window.location.
hostname+/cometd/28.0/,
requestHeaders: { Authorization: OAuth {!$Api.
Session_ID}}
});
j$.cometd.subscribe(/topic/CaseNotifications,
function(message) {
getRemoteCase(message.data.sobject.Id);
});
function getRemoteCase(caseId) {
Visualforce.remoting.Manager.invokeAction(
{!$RemoteAction.
RemoteActionCaseNotificationsController.returnCase},
caseId,
function(result, event){
126
Streaming API
if (event.status) {
document.
getElementById({!$Component.page.block.casenumber}).innerText =
result.CaseNumber;
document.
getElementById({!$Component.page.block.casestatus}).innerText =
result.Status;
document.
getElementById({!$Component.page.block.casepriority}).innerText =
result.Priority;
document.
getElementById({!$Component.page.block.caseowner}).innerText =
result.Owner.Name;
document.
getElementById({!$Component.page.block.accountname}).innerText =
result.Account.Name;
document.
getElementById({!$Component.page.block.contactname}).innerText =
result.Contact.Name;
} else if (event.type === exception) {
document.
getElementById(responseErrors).innerHTML =
event.message + <br/>\n<pre> +
event.where + </pre>;
} else {
document.
getElementById(responseErrors).innerHTML = event.message;
}
},
{escape: true}
);
}
})
</script>
Recall the <apex:outputPanel> with the ID of responseErrors. This panel will render a <div> on
the page where error messages will appear if there is a problem with the query or RemoteAction
method. The error message HTML delivers in the exception conditions of the JavaScript can be
changed to fit your requirements.
You can test this page in the same fashion as the first example. Once the notification is received,
the RemoteAction will fire and update the elements in the DOM with the queried reference data.
Its important to consider the performance of the page while adding the call to the server with
the RemoteAction. There will be a slight delay between the channel notification and providing the
information to the user once the additional reference fields are queried. In high-level performance
benchmarks, this example typically returns data to the page from the RemoteAction within ~200250 ms. This duration will likely extend if the query returns more fields, child relationships, or has
other processing prior to returning the data. While the delay is nominal in this example, it should
not be overlooked as you extend this functionality to support more complex interactions on the
server and client side.
127
We need to add a couple of public variables in the controller to support the incremental Case data
being stored in a collection. Add the following variables to your controller.
128
Streaming API
The caseId will be delivered from the page, and will be used to query for additional Case data. The
Map<Id, CaseWrapper> will be used for multiple reasons. We want to ensure that were displaying
a unique list of Cases to the user, so storing the CaseWrappers in a Map and using the Case ID as
the key will prevent duplicate instances of the wrapper for the same case, even if multiple channel
notifications are received in a short time frame.
Add a method to query for the required Case data, based on the caseId attribute supplied from the
page, and put an instance of the CaseWrapper class in the Map with the Case ID as the key value.
public void addCaseId(){
Case tmpCase = [SELECT Id, caseNumber, status, priority,
owner.name, account.name, contact.name,
(SELECT Id, commentBody, createdBy.Name,
createdDate
FROM CaseComments
ORDER BY CreatedDate desc
LIMIT 5)
FROM Case
WHERE Id = :caseId];
CaseWrapper cw = new CaseWrapper();
cw.c = tmpCase;
cw.ts = system.now();
mCaseWrappers.put(cw.c.Id, cw);
Understanding that a keyset in a Map cannot retain a particular order, well put the Comparable
interface to work to return the list of CaseWrappers to the page in the sequence we desire. Add
another method that returns a list of CaseWrappers from the Map values. The CaseWrapper list
returned by the Map values can be sorted, and then returned to the page.
public List<CaseWrapper> getCases(){
List<CaseWrapper> cases = mCaseWrappers.values();
cases.sort();
return cases;
}
In order to support the Visualforce pie charts, we need to add another class wrapper that will store
the queried summary data from the system based on Case Priority and Case Status. This simple
class wrapper contains a string and integer member variables. Instances of this class wrapper
will hold the name of a case attribute, and the integer will represent how many Cases reflect that
attribute. Add the following code:
public class CaseData{
public string value {get;private set;}
public integer nRecs {get;private set;}
public CaseData(string val, integer n){
value = val;
nRecs = n;
}
}
129
Using Case Status as an example, we can expect to see data similar to the table below:
Status
New
Working
Escalated
nRecs
4
12
1
We need to add two methods that return a list of CaseData wrappers to the page. Well use
an AggregateResult query in each to quickly group on Case Status and Priority, with the count
of records in each group. For more information on AggregateResults, please refer to the Apex
documentation. Add the following code to your controller.
public List<CaseData> getStatusData(){
List<CaseData> statusData = new List<CaseData>();
List<AggregateResult> arStatuses = [SELECT status status,
COUNT(Id) nRecs
FROM Case
WHERE isClosed = false
GROUP BY status];
for(AggregateResult ar : arStatuses){
statusData.add(new CaseData((string)ar.get(status),
(integer)ar.get(nRecs)));
}
return statusData;
}
public List<CaseData> getPriorityData(){
List<CaseData> priorityData = new List<CaseData>();
List<AggregateResult> arPriorities = [SELECT priority
priority,
COUNT(Id) nRecs
FROM Case
WHERE isClosed = false
GROUP BY priority];
for(AggregateResult ar : arPriorities){
priorityData.add(new CaseData((string)
ar.get(priority),
(integer)ar.get(nRecs)));
}
return priorityData;
}
We have completed the controller and can now move on to the Visualforce page. Create a new
Visualforce page named ActionFunctionCaseNotifications with the same <apex:page> tag
attributes and <apex:includeScript > tags as the previous examples. Similar to the other examples,
well need to add a JavaScript block to subscribe to the channel notifications, and pass the
delivered messages to our ActionFunction. Add the following JavaScript to your page below your
IncludeScript tags:
130
Streaming API
<script type=text/javascript>
var j$ = jQuery.noConflict();
j$(document).ready(function() {
// Connect to the CometD endpoint
j$.cometd.init({
url: window.location.protocol+//+window.location.
hostname+/cometd/28.0/,
requestHeaders: { Authorization: OAuth {!$Api.
Session_ID}}
});
j$.cometd.subscribe(/topic/CaseNotifications,
function(message) {
findCaseDetails(message.data.sobject.Id);
});
});
</script>
Notice the findCaseDetails method invocation within the subscribe method. This method will call
out to the ActionFunction, which will be created after adding a few more components to the page.
The page will be segmented into two major sections: the left panel with the pie charts, and the
right panel with the Case details and comments. In order to achieve this presentation, well use an
<apex:panelGrid> with two small style classes for the columns. These classes instruct the panels on
their width, and to align with the top of their containing elements. Add the following styles:
<style>
.panelLeft {
width: 25%;
vertical-align: top;
}
.panelRight {
width: 75%;
vertical-align: top;
}
</style>
Add an <apex:panelGrid> tag as a direct child to the form with two columns, spanning the entire
width of the page, and reflecting the styleClasses defined from above.
<apex:panelGrid columns=2 width=100%
columnClasses=panelLeft,panelRight>
</apex:panelGrid>
131
Create a grouping for the pie charts by adding an <apex:panelGroup> tag inside the PanelGrid
as the content for the first column,. Add the following code inside the PanelGroup to reflect the
AggregateResult data returned for the pie charts from the controller.
<apex:panelGroup >
<apex:pageBlock title=Case Status>
<apex:chart data={!statusData} height=200 width=250
background=#F5F5F5>
<apex:legend position=bottom/>
<apex:pieSeries labelField=value dataField=nRecs
donut=50>
<apex:chartLabel display=middle
orientation=vertical
font=bold 12px Helvetica/>
</apex:pieSeries>
</apex:chart>
</apex:pageBlock>
<apex:pageBlock title=Case Priority>
<apex:chart data={!priorityData} height=200 width=250
background=#F5F5F5>
<apex:legend position=bottom/>
<apex:pieSeries labelField=value dataField=nRecs
donut=50>
<apex:chartLabel display=middle
orientation=vertical
font=bold 12px Helvetica/>
</apex:pieSeries>
</apex:chart>
</apex:pageBlock>
</apex:panelGroup>
This will create two donut-style pie charts representing the number of Cases by Status and Priority.
Any additional elements added to this PanelGroup will appear in the narrow column on the left.
The content for the column on the right is Case detail information and related comments. Add
an <apex:pageBlock> tag after the closing </apex:panelGroup> tag. All content added to this
component will appear in the wide column on the right. The content in this column is generated
by an <apex:repeat> tag that creates PageBlockSections for each instance of a CaseWrapper
returned by the getCases method in the controller. The first PageBlockSection contains Case
Detail information, and the second contains a PageBlockTable to display any comments that have
been created on this case. Each wrapper in the list will have both PageBlockSections to display
content. Notice that the repeat tag is wrapped with an <apex:outputPanel> with styles to handle
content overflow. This helps create a clean page in two ways. First, it prevents the content in the
column on the right from growing exceedingly long and cumbersome looking. Second, and more
importantly, regardless of how long the content becomes, the pie charts will hold a static position
on the page relative to any scrolling occurring in the column on the right.
132
Streaming API
<apex:pageBlock title=Recent Case Updates>
<apex:outputPanel layout=block style=height:500px;overflow-y:scroll;>
<apex:repeat value={!cases} var=cw>
<apex:pageBlockSection title={!cw.c.caseNumber} columns=2>
<apex:outputField value={!cw.c.caseNumber}/>
<apex:outputField value={!cw.c.status}/>
<apex:outputField value={!cw.c.priority}/>
<apex:pageBlockSectionItem >
<apex:outputLabel value=Owner Name/>
<apex:outputField value={!cw.c.owner.name}/>
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem >
<apex:outputLabel value=Account Name/>
<apex:outputField value={!cw.c.account.name}/>
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem >
<apex:outputLabel value=Contact Name/>
<apex:outputField value={!cw.c.contact.name}/>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
<apex:pageBlockSection columns=1>
<apex:pageBlockTable value={!cw.c.CaseComments} var=cc
rendered={!cw.c.CaseComments.size > 0}>
<apex:column value={!cc.createdBy.Name}/>
<apex:column value={!cc.createdDate}/>
<apex:column value={!cc.commentBody}/>
</apex:pageBlockTable>
<apex:outputText value=There are no comments on
this case. rendered={!cw.c.CaseComments.size == 0} style=fontweight:bold/>
</apex:pageBlockSection>
</apex:repeat>
</apex:outputPanel>
</apex:pageBlock>
The final and most important tag that we need to add is the <apex:actionFunction>. This tag will
generate JavaScript upon page load that acts as a conduit between the client JavaScript and the
controller. Add the following code directly after the closing </apex:panelGrid> tag, but still inside
the form:
<apex:actionFunction action={!addCaseId} name=findCaseDetails
rerender=form>
<apex:param name=caseId assignTo={!caseId} value= />
</apex:actionFunction>
133
134
Streaming API
Summary
The examples in this chapter are intended to help illustrate the various ways to implement the
Streaming API in a Visualforce page. In the first example, there is no server-side logic, and
everything is handled in the page upon receipt of the channel notification. If the page is using
data delivered within a channel notification for informational purposes only, this can be a quick
and clean way to deliver the information to the end user. If additional data is needed from the
database for display on the page or user interaction, RemoteAction and ActionFunction methods
can be invoked upon receipt of a channel notification. This allows the page to receive and display
data in a near real-time fashion, while still having access to the controller, view state, or other vital
pieces of Apex code. Ultimately, RemoteAction and ActionFunction implementations interact
with the CometD framework via JavaScript to bridge the gap between a channel notification, and
meaningful interaction with the user on a page. RemoteAction is typically faster, requires more
JavaScript, and has greater flexibility in managing pages than Visualforce AJAX components. On
the other hand, ActionFunction is typically easier to implement, requires little to no JavaScript,
and allows you to rerender other Visualforce components.
When designing a solution that utilizes the Streaming API, consider which objects are
supported, what events should issue notifications so Limits are not exceeded, and how timely
the notifications and interactions should be with the user. Your Push Topic queries should only
consider the information that is needed, rather than issuing notifications for every record thats
committed for a particular object. Its also important to choose the implementation that scales
best over time. If you expect to have your page grow in complexity while maintaining quick
performance, then design your initial solution with RemoteActions. If the page is moderately
simple or response time is not of the utmost importance, then ActionFunction can be the simplest
and fastest solution to implement.
135
136
Chapter 9
Creating Reusable Page Templates
T
Reusing Visualforce Pages
Creating Page Templates
Including Visualforce
pages within a Page
Creating Compositions
Building a Console
With Templates
Custom Components
Now that we have this form we can include it other pages like this:
<apex:page standardController=Article__c >
<!-- do something...
... -->
<apex:include pageName=myForm/>
<!-- do something else...
... -->
</apex:page>
Element
Attributes
Name
composition
template
Required
yes
Subelements
define
yes
related
elements
insert
yes
Description
Used in the main Visualforce page.
Specifies the name of the Visualforce page
containing the template markup.
Defines the section in the main page that will
be templated. Takes a string attribute that is
mapped to the corresponding insert element in
the template page.
Contains the Visualforce components and
content to be inserted.
The code fragment below shows the basic structure for the page.
138
Attribute Name
rendered
template
Description
Used when you want to conditionally display an <apex:component>.
You must be wrap it inside a <apex:outputPanel> component, and
add the conditional expression to its rendered attribute.
The template page used for this component. For this value, specify the
name of the Visualforce page or use merge-field syntax to reference a page
or PageReference.
Attribute Name
name
Description
The name of the insert component into which the content of this define
component should be inserted.
<apex:insert>
Attribute Name
name
Description
The name of the matching define tag that provides the content to be
inserted into this Visualforce page.
139
Articles
Authors
Tags
Articles are related to Authors through a master-detail relationship. In addition Articles are
related to tags through a second master detail relationship. The Articles object contains the
following custom fields and relationships:
Figure 9-1. Custom fields and relationships for the Articles custom object.
Note that for the purposes of this example Authors is created as a custom object. In actual
development we could have just as easily renamed the standard Contacts object and utilized its
extensive features. By creating a custom object, well have to implement many of those features.
Consider reusing standard objects whenever possible.
140
Figure 9-2. Custom fields and relationships for the Authors custom object.
Figure 9-3. Custom fields and relationships for the Tags custom object.
141
Create the template page using <apex:define> to describe your reusable content.
Now lets put this together into a working example. The first step is to create the template that we
can reuse. In this case, the template page has been named contentTemplate.
<apex:page >
<!--Create the Header portion of page -->
<apex:outputPanel layout=none>
<apex:insert name=Header/>
<apex:pageMessages />
</apex:outputPanel>
<!--Console panel portion of page using a PanelGrid with 3
columns -->
<apex:panelGrid Columns=3>
<!--Column 1-->
142
This template defines the overall structure, which consists of two regions: One area for the header
of the page; and second for the body of the page. The page header adds a greeting to the user
and utilizes <apex:pageMessages /> to display validation errors in the event a user misses a
required field or inputs invalid data.
The body is broken up into three columns using the panelGrid component. Each panel
reports on one of three custom objects. Each panel contains an <apex:insert name=some_
identifier> where some_identifier is the name of the component we want to insert. This
identifier references a corresponding <apex:define> element in the main Visualforce page we are
about to create.
Next, its useful to create variables so we dont have to enter lengthy names object references
throughout the code.
<apex:variable value={!Article__c.author__r} var=auth/>
<apex:variable value={!Article__c.tag__r} var=tagit/>
143
<apex:define name=ArticleInfo>
...
</apex:define>
Within this <apex:define>, create a form to contain the first panel, like so:
<apex:form >
<apex:pageBlock mode=detail >
<apex:pageBlockButtons location=top>
<apex:commandButton action={!edit}
value=Edit/>
<apex:commandButton action={!delete}
value=Delete/>
</apex:pageBlockButtons>
<apex:pageBlockSection columns=1 title=Article
Information>
<apex:outputField value={!Article__c.
Name}/>
<apex:outputField value={!Article__c.
Author__c}/>
<apex:outputField value={!Article__c.
Status__c}/>
<apex:outputField value={!Article__c.
Publish_Date__c}/>
<apex:pageBlockSectionItem >
<apex:outputLabel value=Last Modified
By/>
<apex:outputText >
<apex:outputField
value={!Article__c.LastModifiedById}/>,
<apex:outputField
value={!Article__c.LastModifiedDate}/>
</apex:outputText>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
So far weve created a form that displays details about an article including its title, author and
status. Weve also added command buttons that allow us to go to the edit page, or to delete the
article.
144
default__c}/>
other__c}/>
<apex:outputField value={!auth.Cell__c}/>
<apex:outputField value={!auth.email_
<apex:outputField value={!auth.email_
<apex:outputField value={!auth.Work_Phone_
_c}/>
<apex:outputField value={!auth.wiki_link_
_c}/>
biography__c}/>
</apex:pageBlockSection>
<!-- apex:pageblockSection >
<apex:pageBlockSectionItem >
<apex:outputField value={!auth.
</apex:pageBlockSectionItem>
</apex:pageblockSection -->
</apex:pageBlock>
</apex:form>
</apex:define>
Note the use of the URLFOR() action in command buttons for these last two forms. The trained
eye may be wondering why this wasnt required in the first form. The reason is that this page is
linked to the Articles__c standard controller. So, actions implicitly reference Articles__c
without requiring the full object notation. However, actions referencing other objects must name
those objects explicitly. The URLFOR() action is described in detail in Chapter 3.
Finally, heres the third form to display the related tags.
145
146
For example:
<apex:component>
<apex:attribute name=record description=The type of record we are
viewing.
type=Object required=true/>
<apex:pageBlock title=Viewing {!record}>
<apex:detail />
</apex:pageBlock>
</apex:component>
147
Summary
Visualforce allows you to create elements that can be resued in three ways:
Include an existing VF Page using <apex:include>
Defining Templates <apex:composition>
Defining Custom Components
As youll see in the chapter 12, which covers on Mobile Design Templates, Page Templates can
be used to great adavantage. In fact, each method of resuability has its benefits and drawbacks.
For example, custom Components and page templates can both cause your component tree to grow
in size. Be sure to review Chapter 14 regarding performance issues as you design your applications.
148
Chapter 10
Using JavaScript with Visualforce
B
Including JavaScript
in Visualforce Pages
Referencing Components
from JavaScript
Using actionFunction
to Call Apex Methods
from JavaScript
JavaScript Remoting
Building a Spreadsheet
Interface to Access
Force.com Data
Accessing the Analytics
API Using ForceTK
149
This references the $Resource global variable and instantiates it with the name of your JavaScript
file.
You can also use <apex:includeScript> to reference externally-hosted JavaScript files. The
following example shows how to include the jQuery framework from a globally hosted CDN
network.
<apex:page>
<apex:includeScript value=//cdnjs.cloudflare.com/ajax/libs/
jquery/2.0.3/jquery.js/>
</apex:page>
Check your CDN for the latest deployment. Heres a more complete example that uses a
StandardController and references a recordsetVar that can be used to list articles in a database.
<apex:page standardStylesheets=false showHeader=false
sidebar=false standardController=Article__c
recordsetVar=articles>
<apex:stylesheet value=https://ajax.aspnetcdn.com/ajax/jquery.
mobile/1.1.0/jquery.mobile-1.1.0.min.css />
<apex:includeScript value=https://ajax.aspnetcdn.com/ajax/jQuery/
jquery-1.7.2.min.js/>
<apex:includeScript value=https://ajax.aspnetcdn.com/ajax/jquery.
mobile/1.1.0/jquery.mobile-1.1.0.min.js/>
<h1>Article Inventory</h1>
</apex:page>
150
JavaScript
Note the addition of <apex:stylesheet> to include CSS that can be used to style the page. This
differs from <apex:includeScript> even though both are static resources. Weve also added
mobile support by including a second reference jQuery Mobile.
With the script complete, heres the modfications to the first outputPanel, which calls the function,
passing in the checkbox itself, and the DOM ID of the target component.
<apex:outputPanel id=thePanel layout=block>
<apex:insert name=Header/>
<apex:pageMessages />
<label for=checkbox>Hello {!$User.FirstName}</label>
<input id=checkbox type=checkbox
onclick=changeFont(this,{!$Component.thePanel}); />
</apex:outputPanel>
151
At the current level of the component hierarchy where $Component is used; and then
At each successive higher level in the component hierarchy, until a match is found, or the
top-level of the component hierarchy is reached.
There is no backtracking, so if the ID youre trying to match requires a traversal up and then
back down, it wont match.
Returning to our example, the following outputPanel is the target, and contains text that will be
changed.
<apex:actionFunction action={!methodInApexController}
name=methodInJavascript rerender=showstate>
....
<script type=text/javascript>
methodInJavascript();
<script>
152
JavaScript
Attribute Name
action
Attribute Type
ApexPages.Action
focus
String
id
String
immediate
Boolean
name
String
onbeforedomupdate
String
oncomplete
String
rendered
Boolean
reRender
Object
status
timeout
String
Integer
Description
The action method invoked when the
actionFunction is called by a DOM event
elsewhere in the page markup. Use merge-field
syntax to reference the method. For example,
action={!save} references the save method in
the controller. If an action is not specified, the
page simply refreshes.
The ID of the component that is in focus after the
AJAX request completes.
An identifier that allows the actionFunction
component to be referenced by other
components in the page.
A Boolean value that specifies whether the action
associated with this component should happen
immediately, without processing any validation
rules associated with the fields on the page. If
set to true, the action happens immediately and
validation rules are skipped. If not specified, this
value defaults to false.
The name of the JavaScript function that, when
invoked elsewhere in the page markup, causes
the method specified by the action attribute to
execute. When the action method completes, the
components specified by the reRender attribute
are refreshed.
The JavaScript invoked when the
onbeforedomupdate event occurs--that is, when
the AJAX request has been processed, but before
the browsers DOM is updated.
The JavaScript invoked when the result of an
AJAX update request completes on the client.
A Boolean value that specifies whether the
component is rendered on the page. If not
specified, this value defaults to true.
The ID of one or more components that are
redrawn when the result of the action method
returns to the client. This value can be a single
ID, a comma-separated list of IDs, or a merge
field expression for a list or collection of IDs.
The ID of an associated component that
displays the status of an AJAX update request.
See the actionStatus component.
The amount of time (in milliseconds) before an
AJAX update request should time out.
153
JavaScript Remoting
JavaScript remoting in Visualforce lets you call methods in Apex controllers from JavaScript,
enabling you to create pages with complex, dynamic behavior that isnt possible with the standard
Visualforce AJAX components. Like the <apex:actionFunction>, JavaScript Remoting lets
you invoke methods in your Apex controller through JavaScript. Generally, JavaScript Remoting is
a more flexible and performant option when compared to <apex:actionFunction>, because it
allows you to pass parameters and return types into the Apex controller method (with automatic
mapping between Apex and JavaScript types), and allows for asynchronous processing through a
callback.Unlike <apex:actionFunction>, the AJAX request does not include the view state
for the Visualforce page, thus resulting in a faster round-trip. The one drawback is that JavaScript
Remoting requires a little more work.
There are three steps to JavaScript remoting:
1. Create a remote method definition in your Apex controller class. Youll need to add an
annotation to this definition. For example:
//Apex Controller code
@RemoteAction
globalstatic String getItemId(String objectName) { ... }
2. Add the JavaScript to your Visualforce page that calls the remote method.
3. Create a response handler callback function written in JavaScript.
The following example creates a Visualforce page that presents the user with a dialog box that
takes the name of an account. The script then calls the Apex controller, which looks up the account
and reports the results.
Listing 1 presents the controller.
globalwithsharingclassAccountRemoter{
publicStringaccountName{get;set;}
publicstaticAccountaccount{get;set;}
publicAccountRemoter(){}//emptyconstructor
@RemoteAction
globalstaticAccountgetAccount(StringaccountName){
account = [SELECT Id, Name, Phone, Type, NumberOfEmployees
FROMAccountWHEREName=:accountName];
returnaccount;
}
}
Listing 10-1This Apex controller performs a lookup and returns fields from the Account object.
154
JavaScript
Listing 10-2 shows code for the Visualforce page with the remote method call and callback handler:
<apex:page controller=AccountRemoter>
<script type=text/javascript>
function getRemoteAccount() {
var accountName = document.getElementById(acctSearch).value;
Visualforce.remoting.Manager.invokeAction(
{!$RemoteAction.AccountRemoter.getAccount},
accountName,
function(result, event){
if (event.status) {
// Get DOM IDs for HTML and Visualforce elements
like this
document.getElementById(remoteAcctId).innerHTML
= result.Id
document.getElementById(
{!$Component.block.blockSection.secondItem.
acctNumEmployees}
).innerHTML = result.NumberOfEmployees;
} else if (event.type === exception) {
document.getElementById(responseErrors).innerHTML =
event.message + <br/>\n<pre> + event.where
+ </pre>;
} else {
document.getElementById(responseErrors).
innerHTML = event.message;
}
},
{escape: true}
);
}
</script>
<input id=acctSearch type=text/>
<button onclick=getRemoteAccount()>Get Account</button>
<div id=responseErrors></div>
<apex:pageBlock id=block>
<apex:pageBlockSection id=blockSection columns=2>
<apex:pageBlockSectionItem id=firstItem>
<span id=remoteAcctId/>
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem id=secondItem>
<apex:outputText id=acctNumEmployees/>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:page>
156
JavaScript
global static List<DemandPlanning__c> queryDemandPlan(String
year, String pt){
return [select Id, Name, Plan10__c, Diff10__c, Result10__c,
Plan11__c, Diff11__c, Result11__c,
Plan12__c, Diff12__c, Result12__c, Plan1__c, Diff1__c,
Result1__c, Plan2__c, Diff2__c, Result2__c,
Plan3__c, Diff3__c, Result3__c, Plan4__c,
Diff4__c, Result4__c, Plan5__c, Diff5__c, Result5__c,
Plan6__c, Diff6__c, Result6__c, Plan7__c, Diff7__c,
Result7__c, Plan8__c, Diff8__c, Result8__c,
Plan9__c, Diff9__c, Result9__c, DPAccountName__c,
DPAccountName__r.Name, FiscalYear__c, ProductType__c
from DemandPlanning__c where
FiscalYear__c = :year and ProductType__c = :pt
order by DPAccountName__c];
}
...
</apex:page>
Figure 10-2: Cell values are recomputed when values are updated.
157
Listing 10-5 Appending an HTML select element to a cell in its grid in order to show the user a
drop-down list of accounts.
Figure 10-3 shows the result.
158
JavaScript
To save the data back to Salesforce, Lisitng Six iterates through the grid and places each row
in a JavaScript object that matches the Salesforce object. Then, Listing Six creates an array of the
custom object that can be passed to the controller using JavaScript Remoting.
for(var i=0; i<gridData.length; i++){
if(!gridData[i][1]){
//if it is not a subtotal row
demandPlanList.push(
createObjectFromArray(gridData[i], fiscalYear, productType) );
}
}
Visualforce.remoting.Manager.invokeAction({!$RemoteAction.
SpreadsheetController.saveRecords}, demandPlanList,
function(result){
//other process
}
);
@RemoteAction
global static void saveRecords(DemandPlanning__c[]
demandPlanList){
upsert demandPlanList;
}
This could have been done Apex and straight Visualforce, albeit with more complexity. You also
get the performance boost by limiting the view state.
159
If we successfully found a Report ID, we initialize ForceTK and define a variable to hold the
Report data:
if (reportId) {
// Get an instance of the REST API client and set the session ID
var client = new forcetk.Client();
client.setSessionToken({!$Api.Session_ID});
// Well keep the report data around for the life of the page
var report = null;
Now we can go ahead and call ForceTK.ajax() to run the report synchronously. Notice that the
URL path is relative to /services/data:
client.ajax(/v29.0/analytics/
reports/+reportId+?includeDetails=true, function(response){
In the anonymous callback, the first thing we do is save the report data in the report variable, then
write it to a <pre> element on the page. You wouldnt do this in production, but its essential for
development you really want to be able to see the raw report data.
// Save the report data
report = response;
// For debugging
$(#output).text(JSON.stringify(report, null, ));
In this example, Im using one of the standard example reports included in Developer Edition:
Opportunities by Type. In the regular report view, this tells us the total amount, expected revenue
and age of our Opportunities, broken out by their TypeSee Figure 13-4.
What I want to do is show a Pie Chart of a single column of that data, and let the user switch
between columns via a drop-down list, like the one shown in Figure 13-5.
So the first thing we need to do is populate that drop-down from the list of aggregate columns in
the report metadata. The Analytics API documentation discusses the Report data format in some
detail, suffice to say here that Im iterating through the aggregate columns in the report, extracting
the column labels:
160
JavaScript
161
Figure 10-5: Example of the chart wed like to display utilizing data acquired using the Analytics
API.
Now we can render the pie chart. We break this out into its own function,
renderPieChart(),so we can call it both after the data loads, and when the user selects an
aggregate column in the drop-down. After setting a default column, renderPieChart() loads
Once the library has loaded, we can extract labels for the grouping and aggregate columns. The
first entry in the data array that we pass to the Google Charts library has to contain metadata,
rather than the data itself, even though its not shown on the pie chart. Still, we populate the array
with the actual labels, rather than dummy strings, in case we want to reuse the code in a different
context.
// Metadata for the aggregate column
var columnInfo = report.reportExtendedMetadata.
aggregateColumnInfo[report.reportMetadata.aggregates[column]];
// Legends (not shown on pie chart)
var dataArray = [[
report.reportExtendedMetadata.groupingColumnInfo[report.
reportMetadata.groupingsDown[0].name].label,
columnInfo.label
]];
162
JavaScript
Now we can iterate through the report summary data and create the DataTable object that the
Charts API uses:
$.each(report.groupingsDown.groupings, function(index, grouping) {
dataArray.push([grouping.label, report.factMap[index.
toString()+!T].aggregates[column].value]);
});
var data = google.visualization.arrayToDataTable(dataArray);
We want to label the chart according to the data being shown, and format the data appropriately
when were showing currency fields:
var options = {
title: report.attributes.reportName + : + columnInfo.label,
is3D: true,
};
if (columnInfo.dataType === currency) {
var formatter = new google.visualization.NumberFormat({
prefix: $ // This is just sample code - should really
determine the correct currency symbol!
});
formatter.format(data, 1);
}
Now, at last, we can create the chart, and pass in the options and data:
// Create and draw the chart
var chart = new google.visualization.PieChart(document.
getElementById(piechart_3d));
chart.draw(data, options);
And the finished result is shown in Figure 10-6. Again, you wouldnt show the raw JSON report
data to users, but its essential during development for understanding the report structure:
This example showed JavaScript calling the API from a Visualforce page, but the great thing
about ForceTK is that you can use exactly the same code from a page hosted anywhere even in a
hybrid mobile app by just tweaking the initial call to pass a session ID to the ForceTK client.
The Salesforce Analytics REST API unlocks analytics data for developers, and the ability to
call the API from JavaScript on Visualforce pages and elsewhere enables a whole new world of
dynamic apps leveraging the Salesforce Platform.
Summary
There a multitude of things you can do utilizing JavaScript in Visualforce. Indeed, an entire book
could be dedicated to this topic. In this chapter we examined methods for inserting JavaScript into
Visualforce pages that included embedding JavaScript directly into your page, and by importing it
using the <apex:includeScript> tag. This opens the door to JavaScript libraries like jQuery,
Dojo, Angular, Backbone and many others.
163
164
Chapter 11
Building Mobile Apps Using Visualforce,
JavaScript, and HTML5
Design Considerations
Enabling Connected Apps
REST versus JavaScript
Remoting
Extending the Warehouse
App to Mobile
Refactoring Force.
com Apps for Mobile
Design Considerations
For simple applications where all you need is for the
same Visualforce page to display well across different
form factors, a responsive design approach is an
attractive option.
165
Figure 11-1. Designing a Visulaforce tremplate page to act as the mobile template.
166
REST vs Remoting
This first example avoids the complexities of using OAuth by utilizing the JavaScript Remoting
technique described in the previous chapter. This will serve as our data transport mechanism
to push and pull data from @RemoteAction methods in an Apex controller class. An added
advantage of this approach over using the REST services layer, is that we dont have an API call
limit to deal with. However, one trade-off is that we cant leverage the Connected Apps feature of
the Platform Mobile Services.
There are two similar JavaScript libraries that have been made available on GitHub. Forcetk,
which was also presented in the previous chapter uses REST and OAuth. The other library,
Remotetk.js uses JavaScript Remoting and Apex, and well use the latter. Keep in mind that our
web-based mobile application will be very simple, and that the basic user interface, although
attractive and styled for a mobile form factor, will not be very robust. Our effort will be a just
enough exercise; just enough to give you a taste of what kind of apps you can build with a mobile
web approach.
Also keep in mind that there are a number of considerations with taking the HTML5 mobile
web approach. The first is that a mobile web touch experience still does not quite match the
performance and feel of a true native app, but it continues to get closer and closer. Another key
point is that JavaScript will almost always be an integral part of the package.
While JavaScript is a powerful, extensible and challenging programming language to master,
there are numerous libraries and frameworks evolving that provide both structure and leverage
to the language to help prevent your complex business logic from devolving into a pile of
unstructured, unmanageable and brittle code. See Chapter 14 for information on performance and
best practices.
Well use the Force.com Remotetk.js library for our Force.com data transport layer with Apex,
and some simple custom JavaScript code to reside between our HTML markup and the Remotetk.
js generic JavaScript library that will connect to the remoting engine on the Force.com platform.
It may not be as elegant an overall solution, but this less abstract approach may allow you to better
visualize how the various moving parts of a mobile web app all hook together.
Getting Started
Were going to build out the application from the bottom up. We will assume you have completed
the Force.com Tutorial #1, and have the Merchandise__c custom object configured and loaded
with some sample data in a working Force.com org. Well start our app implementation with
the Apex controller and its unit tests, and build out the components based on the dependencies
between them.
Our Code Is In Github: If youd like to follow along, you can cut and past the Apex and
Visualforce code snippets from a GitHub repository at https://github.com/forcementor/MobiWare
and slowly build up the application with the following parts shown in Table 1.
168
Table 11-1. Libraries and componets youll find usefull in this example.
As we add these six components to your org, well review what they do and a little bit about how
they work; the key is to quickly get you a working app that you can then dissect and explore on
your own.
The completed code resides in the repo at https://github.com/forcementor/MobiWare where
you have the options of cloning the repository to your hard drive, or simply navigating to it in a
browser, and by selecting the Raw link in the upper right corner of the code panel. You can do a
Select All and Copy, and then Paste it into your Force.com code editor. All of the code is under
the /src folder, and positioned in the appropriate classes, components, and pages subdirectories
that match the structure of a Force.com metadata package.
By the way, if you are not yet familiar with Git and Github, nows as good a time as any to learn.
It has quickly become a new standard as a source code repository, and you will quickly come
up to speed on its use, as well as become addicted to it as a source code repository. Heres a link
to a great presentation that will explain how Git works, and another link to the Top 10 learning
resources to help you quickly ramp up.
You can choose to build out the components of our app in the Force.com development
environment of your choice. You can use the UI editors under the Setup->Develop area, the
Developer Console Repository, or the Eclipse Force.com IDE; any of these will do just fine. The
manner in which Apex classes, pages and components are named upon creation varies from
devenv to devenv. For example, you must first name the class if in the Force.com IDE or Developer
Console, but in the UI editor, just pasting the class code into the code window will establish the
class name when the code is saved.
Its generally a better practice to build out an application one piece at a time, in an incremental
approach, testing first and then adding more and more functionality with each iteration to insure
that everything is working well before adding too much complexity. For simplicity, this example
focuses on the Viusalforce and runs through the steps of creating each component from the code
base in the Git repository, and well test when were all done. The one exception will be running the
Apex unit tests after creating the controller and its test class.
169
Before we get started, I want to share some words of caution to those of you not too familiar
(yet) with JavaScript:
First: unlike Apex, JavaScript is case sensitive, and also very sensitive about matching block
delimiters (those funny curly braces {} ) and semi-colon statement terminators. Youre mostly
going to be pasting completed code, but if you decide to experiment and change that code, (and
I heartily recommend you should!) youll have to remember to be very careful. Later, down the
road, you can (and should) ramp-up on JavaScript Unit Testing practices with frameworks and
tools such as Jasmine to help you avoid and manage any regression related bugs. You can go here
to find out more.
Second: JavaScript typically fails silently. In other words, you wont necessarily see errors
appearing in the user interface if there are problems with your code at run-time, and you will
have to rely on your browsers developer tools to visualize whats going on behind the scenes to
inspect errors and the HTTP data stream. You can use the developer tools that come with Safari,
or whatever other browser youre using, to monitor console output and to examine your requests
and responses.
170
return null;
171
172
173
$j(#Description__c).val(response.
$j(#Price__c).val(response.Price__c);
$j(#Total_Inventory__c).val(response.Total_
Inventory__c);
$j(#Id).val(response.Id);
//Refresh the page.
$j.mobile.loading( show );
$j.mobile.changePage(#pagDetail, slide,
false, true);
});
null) {
}, errorCallback);
})
.appendTo(#lstMerchandise)
.show();
callback();
}
}, errorCallback);
175
Listing 11-4. Using HTML <DIV> tags to establish different page views on the mobile device.
176
177
178
});
text: MobiWare...,
textVisible: true,
theme: z,
Listing 11-5. Adding the Visualforce components and the boostrapping logic.
179
Figure 11-2. The drill down page ti view the Detail View for an item.
You can also use Chrome, but you will have to configure it to show the unsecured content
each time you launch the mobile page, or configure it to launch with a special flag to turn off
the security check. Thats one extra step I think youd rather avoid, as the fewer moving parts the
better. The second reason is the available developer tools to monitor our JavaScript execution and
our data transport, and the Safari developer tools work great for those tasks.
So, launch your browser and login to your org. To launch your mobile page, modify your URL
address to include only the salesforce.com service instance and add /apex/MobiWare and then
press enter. You should see the MobiWare application load in your browser with a display of up to
twenty Merchandise records from your database.
https://c.{SERVER_NAME}.visual.force.com/apex/MobiWare
You can do the same thing on a mobile device, logging into your development application as usual.
Of course, Salesforce.com will display in a very small presentation, but once logged in, you can
change the URL to add the name of the Apex page.
When youre ready to put a mobile web application into production, it can be launched through
a Force.com Site, exposing mobile Visualforce pages in a secure and sensible manner. Your users
will simply need to navigate to a custom domain that you can be configured with your company
name, and keep in mind that a mobile Visualforce application can also be used to create a mobile
website for your company.
180
Figure 11-3 (previous page). The Edit View, which you can from the previous screen.
On an iPhone, once displayed, users can save the link on their device home screen to launch at
the click of an icon. There are even open source JavaScript libraries that you can add to your app
that will cause a popup bubble message to prompt users to Add to Home. You might try adding
one such library to the MobiWare app as an exercise. You will find one such popular library from
cubiq.org here.
You will need to download their JavaScript library and add it to the MobiWare project as either
a static resource or embedded in a Visualforce component. As mentioned above, Ive kept the
JavaScript for this project in Visualforce components for easy access to the code from the UI editor
or Developer Console, but you are of course free to move it all into static JavaScript library files.
181
183
Many of the examples youll see in the MobilePak reference apps will show you how to fetch
one or more records and perform CRUD operations on them, but theres not too much on how to
launch actions from any particular records context. A specific REST request is typically needed
from any mobile client to invoke such a process in Salesforce. Actions can be easily invoked from
mobile applications, but require a custom Apex action API built into your salesforce org. There are
two general approaches to access your custom API.
If youre using a REST approach from either a native mobile app or a mobile web app using Ajax
for its REST service calls, you will need to create a custom Apex REST resource to invoke your
action on an HTTP POST request from the client. The Apex REST pattern allows you to build
such resources very easily, and invoke them through the Apex REST Service API. However, if
youre leveraging JavaScript Remoting from a mobile web app hosted in a Visualforce page, then
youll require a client side Ajax method to call a global @RemoteAction method on an Apex class.
Regardless of the approach you decide on to make the action request from your mobile app, the
primary task at hand will be to refactor the existing Apex business logic needing to be invoked, so
that it may be accessed from alternate entry points. First well build a new class and a public static
service method. You should make the names of both the class and method self-documenting of
course, but in this case well use generic names.
185
Our service method will expect only the ID of the record as a parameter, and the method will be
responsible for fetching the object as needed from the data layer for processing. It is a best coding
practice to simplify the signatures and parameter collections of the service methods as much as
practical, however its your design decision how to best define the inputs to your methods.
Keep in mind that there are some data type limitations when working with web services, and
you will benefit from any effort to decouple the logic of your classes. In this example, the calling
routines need not concern themselves with anything other than providing the identity of the
record to act upon; they need to know absolutely nothing about the state of the object, or how it
will be processed by the service.
Once the record has been fetched, the method will process the data based on the logic extracted
from the original method in the controller class, and then, based on success or failure, will pass
back an appropriate message.
186
Note that were simply passing to the service method the records ID as derived from the
StandardController. This controller action method will still require logic to determine how to
respond with the appropriate user feedback and navigation based on the message returned from
the service.
Now that we have the service method in place, we can build a simple Apex REST resource that
will provide an external API to the service from an HTTP request from our mobile app or any
other REST based application that needs to call it.
Our Apex resource class method simply wraps a call to the service class method, and passes back
the resulting message. Depending on the signature of the service class method, the Apex REST
resource will need to support whatever parameters are required to invoke it. In our sample case, it
simply needs to pass along the record ID.
@RestResource(urlMapping=/MobileActions/*)
global with sharing class MobileActions {
@HttpPost
global static String doMobileAction(String recordId, String
action) {
if(action == MyCustomAction) {
//Invoke the new service static method and return the
message.
ID tempId = (ID)recordId;
return MyCustomService.MyCustomProcess(tempId);
} else {
return Unknown action: + action;
}
187
}
}
Our final diagram below shows the interaction from the mobile web app now hosted in a
Visualforce page, invoking the service logic via a call from client side Ajax to an @RemoteAction
method on an Apex class.
188
Summary
As you can see, you gain tremendous flexibility when refactoring Apex into a more modular
architecture. This approach allows previously trapped custom business logic to be called as a
service from any entry point, both internal or external to the org.
The same pattern can be applied to similar scenarios, perhaps where a custom button calls a
JavaScript stub, using the Ajax Toolkit to invoke a SOAP Apex web method. Once again, the key is
to extract and isolate the business logic contained in the SOAP web method into a separate service
class, so that it can be called via the SOAP API as well as from a REST service or any Apex code
elsewhere in your org.
Adopting a SOC approach provides maximum flexibility with just a bit of effort to build out a
more structured architecture. You can incrementally refactor your existing code base in bite size
chunks as needed, delivering constant and incremental improvement to your application while
exposing valuable functionality to your shiny new mobile apps.
So...what are you waiting for? Go do it.
189
190
Chapter 12
Building Mobile Apps with
Mobile Design Templates
Getting Started
The best way to get started with the Mobile Design Templates is visit Templates interactive home
page on the Salesforce Mobile Services site at http://www2.developerforce.com/en/mobile/
services/mobile-templates. The page is designed as an interactive walkthrough to the 22 templates.
I say interactive because as you scroll down the page, youll see each template broken down into its
constituent HTML5/CSS3 components. The phone image on the right shows the results of adding
code in realtime. That is, it will update with each template/section making the whole learning
process more visual and easier to follow.
The templates are modular, customizable, open-source CSS3 and HTML5 markup that can be
modified at will to meet your specific UI/UX requirements. Here are some other features:
You can combine these static templates with a JavaScript library like ForceTk or one of the
Mobile Packs for Backbone, Angular or Knockout to provide live data bindings with any
Salesforce backend.
The templates provide cross-platform (iOS, Android etc.) support, courtesy of the use of
standard Web technologies like HTML5, CSS3 and JavaScript.
The base HTML5/CSS3 can be modified to work with any Salesforce object (standard or custom)
in the context of any mobile use case. There are templates to view and edit customer data, view
backend reports, find nearby records, and much more. These starting points for UI and UX design
should dramatically shorten the time it takes to develop a great looking Web or hybrid app on the
Salesforce Platform. Thereafter, you can customize and reuse these templates at will to meet the
specific requirements of your mobile app.
Uses Cases
Mobile templates cover the following popular use cases:
Template
List View Templates List View
Detail View Templates Detail View
192
Description
Provides different visual representations for showing a
list of standard or custom Salesforce records.
Provides various read-only views of a standard or
custom data record. Typically, users will navigate to one
of these detail views by clicking a record in a List View.
Used to capture user input from a phone. The different
form elements included in these templates (phone, date,
number, text, etc.) can be used in any mobile app that
requires users to add or update Salesforce data
Miscellaneous
Description
Provides Google Map-based designs for implementing
the common Find Nearby functionality on a
mobile device. These templates can be combined with
the Geolocation custom field in Salesforce, and its
corresponding SOQL companion, to add geolocation
functionality to any Web or hybrid mobile app.
Provides mobile optimized views of a user s Salesforce
calendar (Tasks and Events).
Provides mobile optimized report views of Salesforce
data. These templates are developed using the openSource charting library and developers can combine
them with the Salesforce Analytics API to add reporting
capabilities to their mobile apps
Use these templates as a starting point to add a
Settings, Splash or About screen to your mobile app.
193
The two most important imports for using the mobile templates in a web page are app.min.css
and main.min.js (note that the GitHub project also has the uncompressed versions of these files
for easier review). These two files respectively define the CSS and minimal JavaScript required to
render the templates. In addition to these two files, the templates also require jQuery 2.0.2 (for
basic DOM manipulation in main.min.js). The Picture List View Template used in this sample also
requires a small JS library (jquery.touchwipe.min.js) to enable touch gestures in the List View.
In addition to the JS and CSS imports, note the use of the HTML5 doctype and the disabling
of the standard stylesheets, header and sidebar in the Visualforce page. This is a way to make the
page mobile optimized and load faster on a mobile device.
194
JavaScript wrapper for the REST API (aka ForceTK) to perform CRUD access to Salesforce
data from a template Visualforce or web page
Mobile Packs for Backbone, Angular or Knockoutwhen the templates are used in an app
built using one of those MV* frameworks.
In the interest of keeping things simple, this example uses JavaScript Remoting in this sample
to query the list of Contact records. Before I dive deeper into the data binding code, lets quickly
review how I store the Contact pics in Salesforce. Since the Contact Standard Object does not have
a native picture field, I created a Rich Text Custom Field (Contact_Pic__c) and used it to upload
thumbnail images to Contact records. You can also upload images as Attachment or Chatter
Files and then use a combination of Formula Fields and trigger/API logic to store and display the
images.
Next, lets review how to query and display a dynamic list of Contact records using the Picture
List View Template.
195
196
This is our first glimpse of the Mobile Design Templates in action. I simply copy pasted this markup
from the Picture List View Template in GitHub (minus the dynamic binding part). I then made a
couple of minor tweaks to the markup to suit my specific use case. For example, removing the gear
icon from the header and updating the menu list (under the <nav class=main-menu> section).
197
And voila, the list view looks a little different. The CSS used in the templates was generated from
Saasand so if youre more comfortable in a tool like Compass, you can modify the Saas files instead.
198
Introducing SObjectData.js
One thing we wanted with our recent additions and updates to the Mobile Packs was to be able
to support the new SmartSync framework. While the original examples for SmartSync included
Backbone.js as a framework, we also wanted to be able to use SmartSync with lower level libraries
like jQuery Mobile. The Mobile Design Templates also require some jQuery to work their
magic. For the jQuery Mobile Pack, and libraries like the Mobile Design Templates, I wrote a new
library to abstract SmartSync without requiring an MVC style library like Backbone or Angular:
SObjectData. This library runs on top of ForceTK (or RemoteTK) and SmartSync to provide easy
data access and handling.
This application will use a very small portion of SObjectData. Al it does is fetch data and
accesses it. This application will be designed for a local hybrid application, designed to work
with our Mobile SDK handle authentication. So with that, and the Mobile Design Template, and
SObjectDataTable 13-2 shows the Prerequisites: :
Mobile SDK
Mobile Design Templates
SObjectData
Authentication
Since this is a local hybrid app well leverage the Mobile SDK for authentication. This will
require setting up a Connected App, and setting the keys correctly using eitherforcedroid
orforceiosto generate a sample application. From there, you can pretty much keep bootconfig.
json from the wwwdirectory and replace the rest with our sample from github. If this is new to
you, check out the Mobile SDK documentation to get up and running.
Querying Data
Accessing your data with SObjectData is done via thefetch method of an SObjectData
instance. Just give it three parameters: type (SOSL or SOQL), query and callback. For our
calendar app, well query based on a time span (either TODAY or THIS_WEEK) and then divide
all the events into different arrays we can association with our template. First we instantiate our
SObjectData. This object will be associated with a specific set of data (in this case our events),
knows the SObjectType and fields associated with that data set and makes it easy to access and
manipulate the data:
199
Now in this instance, we dont need to define either the SObjectType or fields SObjectData will
infer all of that based on the result of our query:
function refreshData(span) {
Events.fetch(soql,Select Id, Subject, StartDateTime,
ActivityDate, ActivityDateTime, DurationInMinutes, Description,
Location from Event WHERE ActivityDate = +span+ ORDER BY
StartDateTime ASC,divideEvents);
200
Note the calendarObj.init call. This is an alternative to theonTemplateReady event that Sandeep
used in his tutorial if we want to refresh only a specific view out of the the Mobile Design
Templates
201
ActivityDateTime to Date
Those of us who have dabbled with Apex Date formats and JavaScript in the past have learned
that the two dont always get along. As part of this app, Ive got a simple function to take an event
record, pull the ActivityDateTime and then convert it to a proper JavaScript date in UTC (to keep
time zone shifts correct):
function activityToDateTime(record){
activity_string = record.ActivityDateTime;
activity_array = activity_string.split(T);
date_array = activity_array[0].split(-);
time_array = activity_array[1].split(.)[0].split(:);
activity_month = parseInt(date_array[1])-1;
return new Date(Date.UTC(date_array[0],activity_month,date_
array[2],time_array[0],time_array[1]));
}
202
Summary
This last example presented a simple calendar application. You can see how libraries
like SObjectData, and the Mobile Design Templates make it easy to get the basics up and
running. And while weve just glossed over the Mobile SDK, you can quickly turn your Visualfore
mobile apps into hybrid apps once youve installed the Mobile SDK. Youll find information on
using this and many other technologies in the Salesforve Mobile App Development Guide on
http://developer.salesforce.com
This chapter also introduced the Mobile Design Templates and presented a second app that
allows you to access Contact records and display them on a mobile device. By combining Mobile
design temapltes with the techniques presented in Chapter 10, you will be well on your way to
building sophisticated enterprise mobile apps that can access data securely in the cloud.
203
204
Chapter 13
Visualforce Performance and Best Practices
Rapidfire Rendering of Visualforce Pages
Sources of Performance
Issues
Optimizing in the
Requirements Phase
Optimizing with Efficient
Page and Controller Design
Optimizing Visualforce
and Apex Code
Optimizing Using Web
Development Best Practices
205
206
Firebug
YSlow
webpagetest.org
Description
The Developer Tools, bundled and available in Chrome, allows web
developers and programmers deep access into the internals of the
browser and their web application.
Firebug integrates with Firefox to put a wealth of web development tools
at your fingertips while you browse. You can edit, debug, and monitor
CSS, HTML, and JavaScript live in any web page.
YSlow analyzes web page performance by examining all the components
on the page, including components dynamically created by JavaScript. It
measures the pages performance and offers suggestions for improvement.
YSlow provides grade, components, and statistics views for the page.
A web site that wroks well for mobile browser testing.
Use automated testing. Testing complex flows in an application can be tedious and produce
inconsistent results. Leverage tools like LoadRunner and Selenium to automate testing.
Automated tests can click on links, enter and retrieve data, and record execution times.
Automation tools may uncover bottlenecks and defects missed by manual testing.
Perform cross-browser testing. Test with as many browsers and versions as possible
Test with large data volumes. You may have designed your pages to avoid unbounded data,
implement pagination, and to filter and display only relevant data. You should still test
with large data volumes. You may encounter scenarios with data skews where particular
users have access to a larger number of records than anticipated. The Developer Force site
includes several excellent articles covering Large Data Volumes including Best Practices
for Deployments with Large Data Volumes at http://wiki.developerforce.com/page/Best_
Practices_for_Deployments_with_Large_Data_Volumes
Adding automated testing to your development process can test for and help you prevent
performance regressions.
208
Description
As described in the introduction of this chapter, utilize
declarative features whenever possible.
Design focused pages that concentrate on individual task
flows, not pages with every possible feature. Probably the
most common Visualforce performance problem customers
experience has to do with overloading a page, trying to
make one or a few pages hold all of the features they need to
manage their business. We recommend instead that pages
be designed with specific tasks, with a sensible workflow and
navigation paths between tasks.
Prioritize by building your pages around a user experience
story and limiting non-essential features. Balance
additional features to a page against usability and business
requirements. Overloaded pages go against many of the
concepts in this document and performance only gets worse
as your business grows.
Description
Pages that show lists of records should never be
unbounded. That is, the code should always limit to a
maximum the number of records displayed. Unbounded
pages will result in longer load times, approach governor
limits, and become unusable as your organization grows
and has more data. Rather than display an unbounded list
of records, use pagination to allow access to records beyond
the first screen. Pagination is a standard user interface
pattern for list views, and support for it is built into the
StandardSetController available to Visualforce pages.
Reduce the number of records displayed on a page by
limiting the data retrieved through SOQL. A good rule
of thumb is 100 records.
SOQL OFFSET also allows you to paginate to a specific
subset of results within SOQL, using your own pagination
logic.
A specific subset of list view pages is the data grid page,
where many records are displayed with their fields available
for editing. Multiply the number of records by the number
of fields per record to display, and you can quickly reach
thousands of input fields, especially for lists without
pagination. Thousands of input components on a page will
potentially overflow the maximum view state size, and will
result in a Visualforce component tree that is slow to process.
Custom data grids encapsulate many of the potential
performance issues in Visualforce. You should consider the
types of users as some may have access to a much larger
number of records. Leverage pagination and filters and,
where possible, make data
Consider displaying only the essential data for a given record
and provide a link for an Ajax-based details box, or even a
separate page, to view the rest.
209
Use pagination and filtering to reduce the number of records displayed on a single page.
Use the with sharing keywords to display only those records the user is allowed to see.
Offload expensive processing using asynchronous tasks when that processing is secondary to
the purpose of the page.
Where possible, make data read-only to reduce the view state size.
Only display essential data for a given record, and provide a link for an Ajax-based details
box or separate page to view the rest.
Filtering
Limit the data coming back from SOQL calls in your Apex controllers. For example, using
AND statements in your WHERE clause, or removing null results. See online help for details on
constructing clauses.
210
View State
Managing, optimizing, and reducing the view state is often the number one task developers need
to do to get their Visualforce pages to perform as desired. To maintain state in a Visualforce page,
the Force.com platform includes the state of components, field values, and controller state in
a hidden form element. This encrypted string is referred to as the view state and has a limit of
135KB. Large view states required longer processing times for each request, including serializing
and de-serializing, and encryption and decryption. By reducing your view state size, your pages
can load quicker and stall less often.
Under Setup | My Personal Information | Personal Information in your org, be sure you have
checked off both the Development Mode and Show View State in Development Mode boxes. This
will now allow you to explore your pages View State in unencrypted mode to examine exactly
what is being stored.
You can monitor view state performance through the View State tab in the development mode
footer and take the following actions:
Use the <apex:actionRegion> tag to submit form data from specific sections of the
Visualforce page.
Use the transient keyword in your Apex controllers for variables that arent essential for
maintaining state and arent necessary during page refreshes.
If you notice that a large percentage of your view state comes from objects used in
controllers or controller extensions, consider refining your SOQL calls to return only data
thats relevant to the Visualforce page.
If your view state is affected by a large component tree, try reducing the number of
components your page depends on.
Even though View State is now stored on the server (since API Version 29.0), it is still important
to manage what is stored there, and keep its size to a minimum in all events it must be less that
the 135K maximum size. Use the transient key word in APEX to remove items from View State.
211
There are other stateless display methods, as well. The key here is that they are not required to be
set within <apex:form> tags, and therefore do not increase View State on your page.
When you are listing out information, can you get by with Read Only information? If so, use the
of records lists, utilize the StandardListController, or the StandardSetController for the page, or
the <apex:EnhancedList> or <apex:ListView> components to let the Visualforce page
maintain the data, and control the paging, freeing up your code and View State for other tasks.
Maybe list the records out ahead of time, and provide a separate Edit button for the User to make
changes later, or let them use the in-line editing feature, if available.
212
In another form of Lazy Loading, consider that the user can only view a certain number of rows
at a time on the screen. If there is only room for ten or twenty rows, why are you waiting to get
thousands back during the initial load? Instead, request a QueryMore construct, or use the SOQL
Offset for future calls, and use the LIMIT clause to only fetch the number of rows that can be shown
at a time. Then your page does not have to wait while the entire collection is assembled at once.
The component tree for this design looks something like this:
Root Component
Page Component
- Form Component
- c:mycomponent
- c:mycomponent2
- InputField Component
- HTML Component
- InputText Component
- HTML Component
- CommandButton Component
214
Action Polling
Action Polling can be an excellent choice for periodic updates. Instead of holding up the initial
page load, you can render the initial information right away, then check periodically for updates
later. However, be sure its action is lightweight, and avoid performing DML, external service calls,
and other resource-intensive operations in its action methods. Extend the interval of the polling
as long as you can get away with: instead of every five seconds, can you wait fifteen seconds, or
even thirty, for the next poll?
Keep in mind that the nature of polling is like the proverbial kids in the backseat, constantly
asking Are we there yet? Similarly, the <apex:ActionPoller> component often generates more
distraction than results. Consider utilizing the Streaming API to refactor it, effectively just getting
the final result when its ready.
Streaming API
When your application is polling SOAP or REST APIs to detect changes, consider refactoring to
utilize the Streaming API. Effectively, it is the equivalent of just telling the kids Were here! one
time, without all the intervening Are we there yet? requests. The Streaming API allows you to set
up PushTopics in advance, and receive specific information back as it occurs, utilizing a publish /
subscribe model to greatly reduce resource consumption.
215
JavaScript Remoting
The AJAX Toolkit and JavaScript Remoting provide similar functionality. Of the two, you will find
that JavaScript Remoting is lighter weight, and easier to set up and get running, so use it if you
dont otherwise have a preference. There are three steps to setting up JavaScript Remoting:
1. Use APEX to write your remote method, and mark it with @RemoteAction
2. In JavaScript set up the call to the remote method on your Visualforce page
3. Write a JavaScript callback function for your Visualforce page to handle the results
Note at the end of the SELECT statement that we access the parameter Map construct on a given
Visualforce page by looking into the collection of Visualforce pages, finding the current page,
getting the Parameters Map, and looking up the value for the ID key.
You can also put your own custom string values into this map. For example,
ApexPages.currentPage().getParameters().set(PurchaseOrder,
123456);
will store that Purchase Order value in the map of the current page. Accessing the Map
on other pages simply requires knowing the name of the Visualforce page you want to
access: Use the name of the specific page, and get its parameters, for example, OtherPage.
getParameters().set(PurchaseOrder, 123456). Similarly, you can reference Maps on other
pages to retrieve information by using the get method: OtherPage.getParameters().
get(PurchaseOrder).
217
Optimizing CSS
While CSS styles can improve the appearance of Visualforce pages, they might also add significant
weight. Follow these tips to optimize your Visualforce page CSS for efficient delivery to the client,
improve caching, and accelerate page display in the browser.
Whenever possible consider externalizing stylesheets, which means taking styles out of the
page itself and placing them in separate CSS files. This practice can increase the number of initial
HTTP requests, but it reduces the size of individual pages. So when possible combine all CSS files
into one or two cascading sheets to reduce the number of HTTP requests. This also allows you to
manage your CSS styles from a centralized location. After the browser caches the stylesheets, the
overall request size decreases.
Other steps you can take are to:
Remove comments and whitespace (spaces, newlines, and tabs), and compress the resulting
file for faster downloads.
Use static resources to serve CSS files, as well as images, JavaScript, and other non-changing
files. Stylesheets and other assets served this way benefit from the caching and content
distribution network (CDN) built into Salesforce.
For pages that dont use Salesforce CSS files, set the <apex:page> tags showHeaders and
standardStylesheets attributes to false. This practice excludes the standard Salesforce CSS
files from the generated page header.
Tip: For Javascript and CSS, it might be burdensome during development to make a change, process
and package it, and deploy. Consider automating this process through a script.
Optimizing JavaScript
Optimize your JavaScript to ensure efficient delivery to the client, improve caching, and accelerate
page display in the browser.
Consider externalizing JavaScript files. This process increases the number of initial HTTP
requests, but it also reduces the size of individual pages and takes advantage of browser caching.
218
Build custom versions of JavaScript libraries with only the functions you need. Many opensource JavaScript libraries, such as jQuery, provide this option, which significantly reduces
the file size.
Combine all JavaScript files into a single file to reduce HTTP requests, and remove duplicate
functions as they might result in more than one HTTP request and waste JavaScript
execution.
Remove comments and whitespace (spaces, newlines, and tabs), and compress the resulting
file for faster downloads.
Put scripts at the bottom of the page. By loading scripts just before the closing </body> tag,
the page can download other components first and render the page progressively.
Note: Only move JavaScript to the bottom of the page if youre certain it doesnt have any adverse
effects on your page. For example, do not move JavaScript code snippets requiring document.write
or event handlers from the <head> element.
Consider including JavaScript files using a standard HTML <script> tag right before your
closing </apex:page> tag instead of using <apex:includeScript>. The <apex:includeScript>
tag places JavaScript right before the closing </head> element, causing the browser to
attempt to load the JavaScript before rendering any other content on the page.
Use static resources to serve JavaScript files, as well as images, CSS, and other non-changing
files. JavaScript and other assets served this way benefit from the caching and content
distribution network (CDN) built into Salesforce.
Tip: For Javascript and CSS, it might be burdensome during development to make a change, process
and package it, and deploy. Consider automating this process through a script.
Summary
Optimizing Visualforce page speed involves a plethora of options, yet there are excellent tools at
our disposal to speed the pathway to success. To summarize best practices, consider:
When it comes to efficiency your options look at your Apex Code, SOQL, and Getter methods.
Look for ways to optimize:
View state
Component hierarchies
Polling
HTML
CSS
JavaScript
Image usage
Pages for Internet Explorer
219
220