Building Visualforce Pages Using Standard Controllers - Part 2

Part 1 of this series introduced standard controllers in Visualforce, and showed how you can materialize the user interface, manage standard UI flow and how to load data into fields. Part 1 ended with a discussion of stateful actions, which are typically bound to Visualforce components directly from the standard controller and are dependent upon a form submission. As it turns out, 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.

(Editors note: This article focuses on the URL page request pattern and the URLFOR() function built into the Salesforce classic web application architecture and Visualforce that predate the Lightning Experience user interface released in 2015. These patterns are not generally applicable to the Single Page Application architecture used in Lightning Experience and the Lightning Component Framework for navigation across user interface views.)

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).

This function can be leveraged to create URLs that support a variety of usage as identified above. For our purposes here, we will focus solely on the usage to invoke actions. The key to this usage is how we leverage the function parameters when declared.

Figure 4: Case Actions

Figure 5: Campaign Actions

Stateless Action Mechanisms

Below is an example usage to create a link to invoke an action. Note that no standard controller needs to be declared on the Visualforce page, nor does the apex:outputLink component need to be nested in an apex:form component. Usage is as follows:

You would replace target with a specified $Action, (or a hardcoded full or partial URL for simple redirection.)

You would 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, (we’ll 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.

$Action Global Variable

The target action in this usage is represented by the global variable $Action, which can access dozens of different kinds of actions that are available to both standard and custom sObjects. Many actions are specific to a particular sObject, and others are applicable to many sObjects such as $Action.Account.New or $Action.MyObject__c.List.

Using this mechanism, actions can be executed with or without the declaration of a standard controller on the Visualforce page. It requires only the evaluation of the URLFOR() function with the $Action global variable and any additional parameter values necessary for the specific action.

You can also declare actions by embedding the URFOR() function call in expressions persisted behind custom buttons and links declared on the detail setup pages for both standard and custom sObjects.

Some of the common sObject actions listed above that are available with direct binding to a standard controller on a page, (such as List, View, Edit and Delete) are also accessible with this mechanism. But those actions which are by nature stateful, (such as Save, QuickSave and Cancel) are not available as they require a form submission.

Many additional specific actions for standard Salesforce sObjects can only be invoked programmatically in this manner, and are not available with direct binding to the standard controller. This mechanism also provides some additional flexibility over stateful actions.

To summarize the features included in stateless actions:

There is no dependency for any standard controller on the page.

There is no dependency for a form component and form submission.

Because there is no form, there is no viewstate on the page, reducing page size.

You may include URL parameter key/value collections when invoking the action.

You may configure the no-override parameter to bypass an action’s pre-existing override.

You may 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 page’s standard controller.

All of these actions leverage the built-in standard controller instruction set. Although not accessed directly from a page’s standard controller instance, they can and should still be considered standard controller functionality.

The StatelessActionDemo Visualforce page below, (also available as a GIST at Github,) 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.

<!--
Note there is NO StandardController, specified sObject or apex:form component.
You MUST set the variables for an Account and Contact ID from your org.
The first column contains a functional $Action link.
The second column shows the Generated URL, note the encoding and any parameters.
Review comments above each link regarding the content of the generated URL displayed.
-->
<apex:page >
<apex:pageBlock title="Stateless Action Demo">
<!--Variables for Account and Contact ids - set these from recods in your org-->
<apex:variable var="contactId" value="003A0000005czjQ" />
<apex:variable var="accountId" value="001A0000005HjZL" />
<apex:outputLabel>Specified Account: {!accountId}</apex:outputLabel>
<br/>
<apex:outputLabel>Specified Contact: {!contactId}</apex:outputLabel>
<br/>
<br/>
<apex:panelGrid columns="2" border="1">
<apex:outputLabel style="font-weight: bold;" >Action</apex:outputLabel>
<apex:outputLabel style="font-weight: bold;" >Generated URL</apex:outputLabel>
<!--New Account
Note there is NO second parameter required on the URLFOR()
Note the standard view Frontdoor URL usage: https://naX.salesforce.com/{ID}/e
The generated retURL redirects back to this page
-->
<apex:outputLink value="{!URLFOR($Action.Account.New)}">New Account</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Account.New)}
</apex:outputLabel>
<!--Change the Account Owner
This is an example of an Account specific $Action
Note the second is the record Id
Note the standard view Frontdoor URL usage: https://naX.salesforce.com/{ID}
Note the optional appended '/a'
The generated retURL redirects back to this page
See the Visualforce documentation for all of the sObject specific $Actions
-->
<apex:outputLink value="{!URLFOR($Action.Account.ChangeOwner, accountId)}">Change Account Owner</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Account.ChangeOwner, accountId)}
</apex:outputLabel>
</apex:panelGrid>
<p/>
<apex:panelGrid columns="2" border="1">
<apex:outputLabel style="font-weight: bold;" >Action</apex:outputLabel>
<apex:outputLabel style="font-weight: bold;" >Generated URL</apex:outputLabel>
<!--New Contact
Note the $Action is NewContact NOT New!
Note there is NO second parameter required on the URLFOR() function
Note the standard new Frontdoor URL usage: https://naX.salesforce.com/{PREFIX}/e
The generated retURL redirects back to this page
-->
<apex:outputLink value="{!URLFOR($Action.Contact.NewContact)}">New Contact</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.NewContact)}
</apex:outputLabel>
<!--View the Contact
Note the standard view Frontdoor URL usage: https://naX.salesforce.com/{ID}
Note the optional appended '/d'
The generated retURL redirects back to this page
-->
<apex:outputLink value="{!URLFOR($Action.Contact.View, contactId)}">View Contact</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.View, contactId)}
</apex:outputLabel>
<!--Edit the Contact, but return to the Contacts standard View page
Note the standard edit Frontdoor URL usage: https://naX.salesforce.com/{ID}/e
The custom retURL redirects to the standard view page.
-->
<apex:outputLink value="{!URLFOR($Action.Contact.Edit, contactId, [retURL=URLFOR('/' + contactId)])}">Edit Contact</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.Edit, contactId, [retURL=URLFOR('/' + contactId)])}
</apex:outputLabel>
<!--Delete the Contact
Note the URL calls https://naX.salesforce.com/setup/own/deleteredirect.jsp?delID={ID}
The generated retURL redirects to the sObject tab /003/o using ID Prefix for Contact
An additional parameter _CONFIRMATIONTOKEN= is generated for additional security
-->
<apex:outputLink value="{!URLFOR($Action.Contact.Delete, contactId)}">Delete Contact</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.Delete, contactId)}
</apex:outputLabel>
<!--Clone the Contacts
Note the standard edit Frontdoor URL usage: https://naX.salesforce.com/{ID}/e
There is an additional parameter to activate the cloning: clone=1
The auto generated retURLback to this page
-->
<apex:outputLink value="{!URLFOR($Action.Contact.Clone, contactId)}">Clone Contact</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.Clone, contactId)}
</apex:outputLabel>
<!--Manual Sharing for the Contact
Note the URL calls https://naX.salesforce.com/p/share/ConSharingDetail?parentId={ID}
The parentId is the Id of this record to be shared
There is no retURL generated unless you add a custom one, redirect will be the standard sObject view
Sharing for the sObject must be set to ReadOnly or Private or an error will display
-->
<apex:outputLink value="{!URLFOR($Action.Contact.Share, contactId)}">Share Contact</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.Share, contactId)}
</apex:outputLabel>
<!--Go to the Contact Home Tab
Note the second parameter on the URLFOR() function is required for $ObjectType
Note the URL calls https://naX.salesforce.com/{PREFIX}/o using ID Prefix for Contact
The generated retURL redirects back to this page
-->
<apex:outputLink value="{!URLFOR($Action.Contact.Tab, $ObjectType.Contact)}">Contact Home Tab</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.Tab, $ObjectType.Contact)}
</apex:outputLabel>
<!--Goto the List for Contact
Note the second parameter on the URLFOR() function is required for $ObjectType
Note the URL calls https://naX.salesforce.com/{PREFIX}/l using ID Prefix for Contact
The generated retURL redirects back to this page
-->
<apex:outputLink value="{!URLFOR($Action.Contact.List, $ObjectType.Contact)}">Contact List</apex:outputLink>
<apex:outputLabel >
{!URLFOR($Action.Contact.List, $ObjectType.Contact)}
</apex:outputLabel>
</apex:panelGrid>
</apex:pageBlock>
</apex:page>

The page will also display the URLs generated by the URLFOR() function for each $Action for your inspection.

Figure 6: StatelessActionDemo display output

URLFOR() Directing to a Visualforce page

In addition to invoking actions, you can also use the URLFOR() function for simple navigation to redirect a user to any web page, including any Visualforce page, by specifying the target as the name of your page prepended with apex/ to create the partial URL.
For example, to redirect to our Visualforce page named CustomAccountPage we would use the following syntax:

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:

URLFOR() and $Action Considerations

Some important considerations regarding URLFOR() and $Action are as follows:

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, there’s 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.

Actions or attributes available on the standard controller for binding to Visualforce components are not discoverable thru the code complete in any development environment as of Winter ‘14; you have to get to know them.

You can’t access URLFOR() and the $Action global variable in Apex – they are for client side 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 it’s parameters can be fragile, and some actions accessed with this mechanism have various dependencies conditionally based on the state of the associated record’s data, the context of the page, or the configuration of the associated sObject. For example, with the Share action, the associated sObject’s 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.

$Actions bound to stateful command components have variable presentation behavior 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 won’t 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.

Adding URL parameters on Actions with URLFOR()

The standard controller is very smart when it comes to redirection after an action has completed, often with alternate behavior based on the outcome of the action. It manages the redirection by automatically embedding a URL parameter named retURL.
If you navigate to a Visualforce page from anywhere, and look carefully in the URL, you will likely see this embedded parameter with an encoded value containing a URL reference to the page upon which the original action was invoked to launch the page. This allows a redirection back to the originating page when a user has completed their action, such as executing a Save or a Cancel operation on an editable page. You should note that not all $Actions embed this parameter.

You can take advantage of this parameter if you want to explicitly redirect the user to an alternate page following completion of your action. You simply need to add the parameter and it’s properly encoded URL value in the parameter collection on the function call. Here’s an example of invoking a New Contact action, which will return to a custom Account page for the current Account record:

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 work tomorrow. So carefully weigh the risks of unpredictable code breakage down the road!

Standard Controllers in Apex

Injecting an ApexPages.StandardController Instance

No chapter on the standard controller would be complete without a discussion of how it can be leveraged when extended by a custom apex controller extension.
The syntax for extending the standard controller with a controller extension is well documented in the Apex Developer’s Guide. A reference to an instance of the page’s standard controller is simply injected as a parameter on the constructor of Apex controller extension class.

Typically, in order to persist the instance, the reference in the class’s constructor is copied to a private class attribute so that it will be available for access in various methods in the extension class.

Fetching the ID or record

A primary usage is to fetch the sObject instance or its id from the passed standard controller, which is done with these instance methods:

getId(): Returns the ID of the record that is currently in context, based on the value of the id query string parameter in the Visualforce page URL.

getRecord(): Returns an instance of an sObject that can then be cast into a concrete sObject to represents the record currently in context, based on the value of the id query parameter in the Visualforce page URL.

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.

Leveraging Standard Controller Actions in Apex

While you will not have access to the comprehensive collection of sObject $Actions available from within your Visualforce page, the stateful common actions on the page’s standard controller are available as methods on the standard controller instance passed into the class constructor.

It is often useful to leverage these common actions from within Apex action methods for their existing action behavior as well as their default navigation or overrides. Each will properly redirect the user to the default page location following the completion of associated operation.

The return type of each of these instance methods is an ApexPages.PageReference, which can be used to control subsequent navigation upon completion of the action. Remember that you can also override default behavior for each action by setting an override in the sObject detail setup page.

Here are the available actions available as instance methods on the StandardController class:

view(): Returns the PageReference associated with the sObject’s view action (standard detail page by default) for the designated record.

save(): Saves changes and returns the PageReference associated with the sObject’s view action (by default standard detail page) for the updated record refreshed with the updated data.

cancel(): Abandon’s any changes and returns the PageReference associated with the cancel action (by default the standard detail page) for the designated record refreshed with the original data.

delete(): Deletes the record and returns the PageReference associated with the delete action (by default the sObjects Home page).

Take note that there are a few other Apex classes available that extend the StandardController class:

ApexPages.IdeaStandardController

ApexPages.IdeaStandardSetController

ApexPages.KnowledgeArticleVersionStandardController

Each provides the same base functionality of the StandardController or StandardSetController class, but adds specific methods to support additional actions or functionality required by the associated standard sObjects.

We simply want to call your attention to them as they represent an example of where the Apex language has been extended to provide additional system delivered standard controller functionality.

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.
Here is the standard page scenario. Two users, (we’ll call them Frank and Sally,) concurrently attempt to edit the same record. Frank saves his changes first, and when Sally attempts to save her changes, (which could overwrite Frank’s saved changes,) Sally will be directed to an error page which will display the following message:

Your Changes Cannot Be Saved The record you were editing was modified by Frank during your edit session.

Please re-display the record before editing again.

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 Frank’s save operation.

Here’s 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, Sally’s subsequent save operation will result in an overwrite of Frank’s newly saved data. No error or warning will be displayed to her or anyone else, and no one will know that Frank’s 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.

Action Method Navigation with ApexPages.PageReference

If you need to override an override as set in a setup page for an sObject, Apex controller extension methods allow you to define the final redirection with a returned ApexPages.PageReference instance from each action method.

You can dynamically construct full or partial URLs to direct the user to a desired page. You can also leverage the Frontdoor URL syntax mentioned earlier, or the return value of the standard controller instance methods for the appropriate redirection.

The key point here is that you have many, many options including adding parameter values on the PageReference, even those derived from standard controller instance methods.

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.

This includes actions as well as records, so if you decide to move to a fully customized controller, you will have to carefully manage what gets exposed to which user. You will need to provide logic for sObject CRUD operations, field level security and all other actions necessary to manage functionality - all of which can be a substantial task. This is one of the greatest benefits of leveraging a standard controller in Visualforce.

This article was excerpted from Visualforce in Practice, published by salesforce.com, November 2013 and released at Dreamforce '13.