Advanced Examples. salesforce Visualforce Developer Guide
Below you will find brief information for Visualforce Developer Guide. The guide offers comprehensive details on Visualforce which allows developers to build sophisticated user interfaces for Salesforce applications, providing deep customization and control over the user experience.
Advertisement
Advertisement
CHAPTER 8
Advanced Examples
The examples in
are considered beginning examples, and primarily use only Visualforce markup. Advanced examples use Force.com Apex code in addition to Visualforce markup.
Creating Your First Custom Controller
Up through this point, all of the examples in this tutorial have used the standard Account controller to define the underlying logic of each page. Visualforce, however, allows you to add your own logic and navigation controls to a page by defining a custom controller.
The following topics walk through the basics of creating a custom controller class and defining class methods that can interact with
Visualforce markup:
•
Creating a Custom Controller Class
•
•
•
•
Mass-Updating Records with a Custom List Controller
Note: You can add, edit, or delete Apex using the Salesforce user interface only in a Developer Edition, a Salesforce Enterprise
Edition trial organization, or a sandbox organization. In a Salesforce production organization, you can only make changes to Apex using either the Force.com Migration Tool or the Force.com API compileAndTest
call.
Creating a Custom Controller Class
A custom controller is simply an Apex class. For example, the following code is a valid, though ineffective, controller class: public class MyController {
}
You can create a controller class and add it to your page in two different ways:
•
Add the controller attribute to your page and use a “quick fix” to create the controller class on the fly:
1. In the page editor, add the controller attribute to the
<apex:page >
tag. For example:
<apex:page controller= "MyController" >
<apex:pageBlock title= "Hello {!$User.FirstName}!" >
This is your new page.
</apex:pageBlock >
</apex:page >
2. Use the quick fix option to automatically create a new Apex class named MyController.
•
Create and save the controller class in the Apex editor of your choice, and then reference it in your page:
116
Advanced Examples Defining Getter Methods
1.
In the application, from Setup, enter “Apex Classes” in the
Quick Find
box, then select Apex Classes and click New to create a new class.
2. Return to your page and add the controller
attribute to the
<apex:page >
tag as described in the example above.
Note: A page can only reference one controller at a time. You can’t use both the standardController
attribute and the controller
attribute in an
<apex:page >
tag.
As soon as you save a page that references a valid custom controller, a second Controller editor tab is available next to the Page Editor.
This editor allows you to toggle back and forth between your page markup and the Apex that defines the page’s logic.
The Custom Controller Editor
Defining Getter Methods
One of the primary tasks for a Visualforce controller class is to give developers a way of displaying database and other computed values in page markup. Methods that enable this type of functionality are called getter methods, and are typically named get
Identifier
, where
Identifier
is the name for the records or primitive values returned by the method.
For example, the following controller has a getter method for returning the name of the controller as a string: public class MyController { public String getName() { return 'MyController' ;
}
}
To display the results of a getter method in a page, use the name of the getter method without the get
prefix in an expression. For example, to display the result of the getName
method in page markup, use
{!name}
:
<apex:page controller= "MyController" >
<apex:pageBlock title= "Hello {!$User.FirstName}!" >
This is your new page for the {!name} controller.
</apex:pageBlock >
</apex:page >
117
Advanced Examples Defining Getter Methods
In earlier examples that used the standard Account controller, the pages displayed values from an account record specified in the URL
(with the id
query string parameter) by using an
{!account.
<fieldName>}
expression. This was possible because the Account standard controller includes a getter method named getAccount
that returns the specified account record. We can mimic this functionality in a custom controller with the following code: public class MyController { public String getName() { return 'MyController' ;
} public Account getAccount() { return [select id, name from Account where id = :ApexPages.currentPage().getParameters().get( 'id' )];
}
}
Note:
For this example to render properly, you must associate the Visualforce page with a valid account record in the URL. For example, if
001D000000IRt53
is the account ID, the resulting URL should be: https://
Salesforce_instance
/apex/MyFirstPage?id=001D000000IRt53
The getAccount
method uses an embedded SOQL query to return the account specified by the id
parameter in the URL of the page. To access id
, the getAccount
method uses the
ApexPages
namespace:
•
First the currentPage
method returns the
PageReference
instance for the current page.
PageReference
returns a reference to a Visualforce page, including its query string parameters.
•
Using the page reference, use the getParameters
method to return a map of the specified query string parameter names and values.
•
Then a call to the get
method specifying id
returns the value of the id
parameter itself.
A page that uses the MyController controller can display either the account name
or id
fields with an
{!account.name}
or
{!account.id}
expression, respectively. Only those fields are available to the page because those were the only fields returned by the SOQL query in the controller.
To more closely mimic the standard Account controller, we can add the tabStyle
attribute to the
<apex:page >
tag to give the page the same styling as other account pages. The markup for the page now looks like this:
<apex:page controller= "MyController" tabStyle= "Account" >
<apex:pageBlock title= "Hello {!$User.FirstName}!" >
This is your new page for the {!name} controller.
<br/ >
You are viewing the {!account.name} account.
</apex:pageBlock >
</apex:page >
118
Advanced Examples
Using a Custom Controller to Display Values on a Page
Defining Action Methods
Defining Action Methods
Action methods perform logic or navigation when a page event occurs, such as when a user clicks a button, or hovers over an area of the page. Action methods can be called from page markup by using
{! }
notation in the action
parameter of one of the following tags:
•
creates a button that calls an action
•
creates a link that calls an action
•
•
makes an event (such as “onclick”, “onmouseover”, and so on) on another, named component, call an action
•
defines a new JavaScript function that calls an action
•
calls an action when the page is loaded
For example, in the sample page described in
Using Input Components in a Page on page 24, a command button is bound to the
save
method in the Account standard controller. We can adapt that previous example so that it now uses the MyController custom controller:
<apex:page controller= "MyController" tabStyle= "Account" >
<apex:form >
<apex:pageBlock title= "Hello {!$User.FirstName}!" >
You are viewing the {!account.name} account.
<p/ >
Change Account Name: <p/ >
</apex:pageBlock >
</apex:form >
</apex:page >
<apex:inputField value= "{!account.name}" /> <p/ >
<apex:commandButton action= "{!save}" value= "Save New Account Name" />
119
Advanced Examples Defining Action Methods
Note: Remember, for this page to display account data, the ID of a valid account record must be specified as a query parameter in the URL for the page. For example: https://
Salesforce_instance
/apex/myPage?id=001x000xxx3Jsxb
After saving the page above, the Visualforce editor offers a “quick fix” option to add the save
method to the MyController class. If you click the quick fix link, MyController now looks like this: public class MyController { public PageReference save() { return null ;
} public String getName() { return 'MyController' ;
} public Account getAccount() { return [select id, name from Account where id = :ApexPages.currentPage().getParameters().get( 'id' )];
}
}
The save
method that is generated by the quick fix takes the standard signature for an action method: it is public, returns a PageReference, and contains no arguments.
Ultimately, the save
method definition must update the database with new account values, but first we must define a member variable to save the account information that is retrieved from the database. Without a member variable for the account, the record retrieved from the database does not persist after its values are used to render the page, and the user's updates to the record cannot be saved.
To introduce this member variable, two parts of the controller code need to change:
•
The member variable must be added to the class
•
The member variable must be set when getAccount
performs the initial query public class MyController {
Account account; public PageReference save() { return null ;
} public String getName() { return 'MyController' ;
} public Account getAccount() { if (account == null ) account = [select id, name, site from Account where id = :ApexPages.currentPage().getParameters().get( 'id' )]; return account;
}
}
120
Advanced Examples Defining Navigation Methods
Now that the member variable is in place, all that the save
method needs to do is update the database: public class MyController {
Account account; public PageReference save() { update account; return null ;
} public String getName() { return 'MyController' ;
} public Account getAccount() { if (account == null ) account = [select id, name, site from Account return account; where id = :ApexPages.currentPage().getParameters().get( 'id' )];
}
}
A more robust solution for save
might catch various exceptions, look for duplicates, and so on. Since this is meant to be a simple example, those details have been left out.
To test this page, change the value in the
Change Account Name
field and click Save New Account Name. As with the standard
Account controller example, the page simply refreshes with the new account name. In the next example, we will extend the save action so that instead of refreshing the current page, it navigates the user to a different confirmation page.
Note: For the page to render properly, you must specify a valid account ID in the URL. For example, if
001D000000HRgU6
is the account ID, use the following URL: https://
Salesforce_instance
/apex/MyFirstPage?id=001D000000HRgU6
Defining Navigation Methods
In addition to performing database updates and other computations, custom controller action methods can navigate users to a different page by returning a PageReference object.
A PageReference is a reference to an instantiation of a page. Among other attributes, PageReferences consist of a URL and a set of query parameter names and values.
In a custom controller or controller extension, you can refer to or instantiate a PageReference in one of the following ways:
121
Advanced Examples Defining Navigation Methods
•
Page.
existingPageName
Refers to a PageReference for a Visualforce page that has already been saved in your organization. By referring to a page in this way, the platform recognizes that this controller or controller extension is dependent on the existence of the specified page and will prevent the page from being deleted while the controller or extension exists.
•
PageReference pageRef = new PageReference( '
partialURL
');
Creates a PageReference to any page that is hosted on the Force.com platform. For example, setting
'partialURL'
to
'/apex/HelloWorld'
refers to the Visualforce page located at http://mySalesforceInstance/apex/HelloWorld
. Likewise, setting
'
partialURL'
to
'/' + '
recordID' refers to the detail page for the specified record.
This syntax is less preferable for referencing other Visualforce pages than
Page.
existingPageName
because the PageReference is constructed at runtime, rather than referenced at compile time. Runtime references are not available to the referential integrity system. Consequently, the platform doesn't recognize that this controller or controller extension is dependent on the existence of the specified page and won't issue an error message to prevent user deletion of the page.
•
PageReference pageRef = new PageReference( '
fullURL
');
Creates a PageReference for an external URL. For example:
PageReference pageRef = new PageReference( 'http://www.google.com' );
For this example, suppose you want to redirect a user to another page with a new URL after he or she clicks Save. To do this, first create a second page named mySecondPage by navigating to the following URL and using the quick fix: https://
Salesforce_instance
/apex/mySecondPage
Then add the following markup to mySecondPage. For simplicity, just use the following standard-controller-based page that was defined earlier in the tutorial:
<apex:page standardController= "Account" >
Hello {!$User.FirstName}!
<p >You are viewing the {!account.name} account.
</p >
</apex:page >
Now return to the original page that you built in Defining Action Methods
on page 119 and make sure that you have specified an account id
query parameter in the URL. Edit the save
method in the controller so that it returns a PageReference to the new page you just created, “mySecondPage”: public class MyController {
Account account; public PageReference save() { update account;
PageReference secondPage = Page.mySecondPage; secondPage.setRedirect( true ); return secondPage;
} public String getName() { return 'MyController' ;
}
122
Advanced Examples Creating a Wizard public Account getAccount() { if (account == null ) account = [select id, name, site from Account return account; where id = :ApexPages.currentPage().getParameters().get( 'id' )];
}
}
Notice in the code above that the redirect
attribute for the PageReference is set to true. If this attribute is not set, the PageReference is returned to the browser, but no navigation occurs—the URL for the original page remains the same. If you want to change the URL as a result of navigation, you have to set the redirect
attribute.
If you test the page now, clicking Save New Account Name navigates to mySecondPage, but the data context is lost—that is, no value is available for
{!account.name}
. The reason for this is that when a redirect occurs the controller clears the context state. Consequently we need to reset the id
query string parameter in the PageReference's parameter map: public class MyUpdatedController {
Account account; public PageReference save() { update account;
PageReference secondPage = Page.mySecondPage; secondPage.setRedirect( true ); secondPage.getParameters().put( 'id' ,account.id); return secondPage;
} public String getName() { return 'MyController' ;
} public Account getAccount() { if (account == null ) account = [select id, name, site from Account return account; where id = :ApexPages.currentPage().getParameters().get( 'id' )];
}
}
Creating a Wizard
Having learned about the essential features of Visualforce markup and controllers, this final example shows how they can be used together to create a custom, three-step wizard that allows users to create an opportunity at the same time as a related contact, account, and contact role:
•
The first step captures information related to the account and contact
•
The second step captures information related to the opportunity
•
The final step shows which records will be created and allows the user to save or cancel
To implement this wizard, we must define three pages for each of the three steps in the wizard, plus a single custom controller that sets up navigation between each of the pages and tracks the data that the user enters.
123
Advanced Examples Creating a Wizard
Important: Data that's used across several Visualforce pages must be defined within the first page, even if that page isn't using the data. For example, if a field is necessary on pages two and three of a three-step process, page one must also contain the field.
You can hide this field from the user by setting the rendered
attribute of the field to false
.
The code for each of these components is included in the sections below, but first you need to understand the best procedure for creating them because each of the three pages references the controller, and the controller references each of the three pages. In what appears to be a conundrum, you cannot create the controller without the pages, but the pages have to exist to refer to them in the controller.
We can work out of this problem by first defining pages that are completely empty, then creating the controller, and then adding markup to the pages. Consequently, the best procedure for creating the wizard pages and controller is as follows:
1. Navigate to the URL for the first page, https://
Salesforce_instance/apex/opptyStep1
, and click Create Page
opptyStep1.
2. Repeat the step above for the other pages in the wizard, opptyStep2
and opptyStep3
.
3. Create the newOpportunityController
controller by adding it as an attribute to the
<apex:page >
tag on one of your pages (for example,
<apex:page controller= "newOpportunityController" >
, and clicking Create Apex
controller newOpportunityController. Paste in all of the controller code and click Save.
4. Now return to the editors for the three pages that you created and copy in their code. The wizard should now work as expected.
Note:
Although you can create an empty page, the reverse is not true—in order for a page to refer to a controller, the controller has to exist with all of its methods and properties.
The Opportunity Wizard Controller
The following Apex class is the controller for all three pages in the New Customer Opportunity wizard: public class newOpportunityController {
// These four member variables maintain the state of the wizard.
// When users enter data into the wizard, their input is stored
// in these variables.
Account account;
Contact contact;
Opportunity opportunity;
OpportunityContactRole role;
// The next four methods return one of each of the four member
// variables. If this is the first time the method is called,
// it creates an empty record for the variable.
public Account getAccount() { if (account == null ) account = new Account(); return account;
} public Contact getContact() { if (contact == null ) contact = new Contact(); return contact;
} public Opportunity getOpportunity() { if (opportunity == null ) opportunity = new Opportunity();
124
Advanced Examples Creating a Wizard
} return opportunity; public OpportunityContactRole getRole() { if (role == null ) role = new OpportunityContactRole(); return role;
}
// The next three methods control navigation through
// the wizard. Each returns a PageReference for one of the three pages
// in the wizard. Note that the redirect attribute does not need to
// be set on the PageReference because the URL does not need to change
// when users move from page to page.
public PageReference step1() { return Page.opptyStep1;
} public PageReference step2() { return Page.opptyStep2;
} public PageReference step3() { return Page.opptyStep3;
}
// This method cancels the wizard, and returns the user to the
// Opportunities tab public PageReference cancel() {
PageReference opportunityPage = new ApexPages.StandardController(opportunity).view(); opportunityPage.setRedirect( true ); return opportunityPage;
}
// This method performs the final save for all four objects, and
// then navigates the user to the detail page for the new
// opportunity.
public PageReference save() {
// Create the account. Before inserting, copy the contact's
// phone number into the account phone number field.
account.phone = contact.phone; insert account;
// Create the contact. Before inserting, use the id field
// that's created once the account is inserted to create
// the relationship between the contact and the account.
contact.accountId = account.id; insert contact;
// Create the opportunity. Before inserting, create
// another relationship with the account.
opportunity.accountId = account.id;
125
Advanced Examples Creating a Wizard insert opportunity;
// Create the junction contact role between the opportunity
// and the contact.
role.opportunityId = opportunity.id; role.contactId = contact.id; insert role;
// Finally, send the user to the detail page for
// the new opportunity.
PageReference opptyPage = new ApexPages.StandardController(opportunity).view(); opptyPage.setRedirect( true );
} return opptyPage;
}
Step One of the Opportunity Wizard
The following code defines the first page of the wizard ( opptyStep1
) in which data about the associated contact and account is gathered from the user:
<apex:page controller= "newOpportunityController" tabStyle= "Opportunity" >
<script > function confirmCancel() { var isCancel = confirm( "Are you sure you wish to cancel?" ); if (isCancel) return true; return false;
}
</script >
<apex:sectionHeader title= "New Customer Opportunity" subtitle= "Step 1 of 3" />
<apex:form >
<apex:pageBlock title= "Customer Information" mode= "edit" >
<!-- The pageBlockButtons tag defines the buttons that appear at the top and bottom of the pageBlock. Like a facet, it can appear anywhere in a pageBlock, but always defines the button areas.-->
<!-- The Next button contained in this pageBlockButtons area calls the step2 controller method, which returns a pageReference to the next step of the wizard. -->
<apex:pageBlockButtons >
<apex:commandButton action= "{!step2}" value= "Next" />
<apex:commandButton action= "{!cancel}" value= "Cancel" onclick= "return confirmCancel()" immediate= "true" />
</apex:pageBlockButtons >
<apex:pageBlockSection title= "Account Information" >
<!-- Within a pageBlockSection, inputFields always display with their corresponding output label. -->
126
Advanced Examples Creating a Wizard
<apex:inputField id= "accountName" value= "{!account.name}" />
<apex:inputField id= "accountSite" value= "{!account.site}" />
</apex:pageBlockSection >
<apex:pageBlockSection title= "Contact Information" >
<apex:inputField id= "contactFirstName" value= "{!contact.firstName}" />
<apex:inputField id= "contactLastName" value= "{!contact.lastName}" />
<apex:inputField id= "contactPhone" value= "{!contact.phone}" />
</apex:pageBlockSection >
</apex:pageBlock >
</apex:form >
</apex:page >
Notice the following about the markup for the first page of the wizard:
•
The
<apex:pageBlock >
tag can take an optional
<apex:pageBlockButtons >
child element that controls the buttons that appear in the header and footer of the component. The order in which the
<apex:pageBlockButtons >
tag appears in the
<apex:pageBlock >
body does not matter. In this page of the wizard, the
<apex:pageBlockButtons >
tag includes the Next button that appears in the footer of the page block area.
•
The wizard relies on JavaScript code to display a dialog box asking if a user wants to navigate away when clicking the Cancel button.
•
In this page of the wizard, the Next button calls the step2
method in the controller, which returns a
PageReference
to the next step of the wizard:
<apex:pageBlockButtons >
<apex:commandButton action= "{!step2}" value= "Next" />
</apex:pageBlockButtons >
Command buttons must appear in a form, because the form component itself is responsible for refreshing the page display based on the new
PageReference
.
•
An
<apex:pageBlockSection >
tag organizes a set of data for display. Similar to a table, an
<apex:pageBlockSection > consists of one or more columns, each of which spans two cells—one for a field's label, and one for its value. Each component found in the body of an
<apex:pageBlockSection >
tag is placed into the next cell in a row until the number of columns is reached.
At that point, the next component wraps to the next row and is placed in the first cell.
Some components, including
<apex:inputField >
, automatically span both cells of a page block section column at once, filling in both a field's label and value. For example, in the Contact Information area of this page, the
First Name
field is in the first column, the
Last Name
field is in the second column, and the
Phone
field wraps to the first column of the next row:
<apex:pageBlockSection title= "Contact Information" >
<apex:inputField id= "contactFirstName" value= "{!contact.firstName}" />
<apex:inputField id= "contactLastName" value= "{!contact.lastName}" />
<apex:inputField id= "contactPhone" value= "{!contact.phone}" />
</apex:pageBlockSection >
•
The value
attribute on the first
<apex:inputField >
tag in the preceding code excerpt assigns the user's input to the firstName field of the contact record that's returned by the getContact
method in the controller.
Your page should look like this:
127
Advanced Examples
Step 1 of the New Customer Opportunity Wizard
Creating a Wizard
Step Two of the Opportunity Wizard
The following code defines the second page of the wizard ( opptyStep2
) in which data about the opportunity is gathered from the user:
<apex:page controller= "newOpportunityController" tabStyle= "Opportunity" >
<script > function confirmCancel() { var isCancel = confirm( "Are you sure you wish to cancel?" ); if (isCancel) return true; return false;
}
</script >
<apex:sectionHeader title= "New Customer Opportunity" subtitle= "Step 2 of 3" />
<apex:form >
<apex:pageBlock title= "Opportunity Information" mode= "edit" >
<apex:pageBlockButtons >
<apex:commandButton action= "{!step1}" value= "Previous" />
<apex:commandButton action= "{!step3}" value= "Next" />
<apex:commandButton action= "{!cancel}" value= "Cancel" onclick= "return confirmCancel()" immediate= "true" />
</apex:pageBlockButtons >
<apex:pageBlockSection title= "Opportunity Information" >
<apex:inputField id= "opportunityName" value= "{!opportunity.name}" />
<apex:inputField id= "opportunityAmount" value= "{!opportunity.amount}" />
<apex:inputField id= "opportunityCloseDate" value= "{!opportunity.closeDate}" />
<apex:inputField id= "opportunityStageName" value= "{!opportunity.stageName}" />
<apex:inputField id= "contactRole" value= "{!role.role}" />
</apex:pageBlockSection >
</apex:pageBlock >
</apex:form >
</apex:page >
Notice that although the markup for placing the
Close Date
,
Stage
, and
Role for Contact
fields on the form is the same as the other fields, the
<apex:inputField >
tag examines the data type of each field to determine how to display it. For example, clicking in the
Close Date
text box brings up a calendar from which users can select the date.
128
Advanced Examples
Your page should look like this:
Step 2 of the New Customer Opportunity Wizard
Creating a Wizard
Step Three of the Opportunity Wizard
The last block of code defines the third page of the wizard ( opptyStep3
) in which all inputted data is displayed. The user can decide to save the operation or return to the previous step:
<apex:page controller= "newOpportunityController" tabStyle= "Opportunity" >
<script > function confirmCancel() { var isCancel = confirm( "Are you sure you wish to cancel?" ); if (isCancel) return true; return false;
}
</script >
<apex:sectionHeader title= "New Customer Opportunity" subtitle= "Step 3 of 3" />
<apex:form >
<apex:pageBlock title= "Confirmation" >
<apex:pageBlockButtons >
<apex:commandButton action= "{!step2}" value= "Previous" />
<apex:commandButton action= "{!save}" value= "Save" />
<apex:commandButton action= "{!cancel}" value= "Cancel" onclick= "return confirmCancel()" immediate= "true" />
</apex:pageBlockButtons >
<apex:pageBlockSection title= "Account Information" >
<apex:outputField value= "{!account.name}" />
<apex:outputField value= "{!account.site}" />
</apex:pageBlockSection >
<apex:pageBlockSection title= "Contact Information" >
<apex:outputField value= "{!contact.firstName}" />
<apex:outputField value= "{!contact.lastName}" />
<apex:outputField value= "{!contact.phone}" />
<apex:outputField value= "{!role.role}" />
</apex:pageBlockSection >
<apex:pageBlockSection title= "Opportunity Information" >
<apex:outputField value= "{!opportunity.name}" />
<apex:outputField value= "{!opportunity.amount}" />
129
Advanced Examples Advanced Visualforce Dashboard Components
<apex:outputField value= "{!opportunity.closeDate}" />
</apex:pageBlockSection >
</apex:pageBlock >
</apex:form >
</apex:page >
Notice that the third page of the wizard simply writes text to the page with
<apex:outputField >
tags.
Your final page should look like this:
Step 3 of the New Customer Opportunity Wizard
Advanced Visualforce Dashboard Components
Visualforce pages can be used as dashboard components. A dashboard shows data from source reports as visual components, which can be charts, gauges, tables, metrics, or Visualforce pages. The components provide a snapshot of key metrics and performance indicators for your organization. Each dashboard can have up to 20 components.
Visualforce pages that use the Standard Controller
can’t be used in dashboards. To be included in a dashboard, a Visualforce page must
have either no controller, use a custom controller , or reference a page bound to the
. If a Visualforce page does not meet these requirements, it does not appear as an option in the dashboard component
Visualforce Page
drop-down list.
The following example shows a Visualforce page that can be used within a dashboard and that uses a custom list controller. It displays all of the open cases associated with a contact named “Babara Levy”:
<apex:page controller= "retrieveCase" tabStyle= "Case" >
<apex:pageBlock >
{!contactName} 's Cases
<apex:pageBlockTable value= "{!cases}" var= "c" >
</apex:pageBlockTable >
</apex:pageBlock >
</apex:page >
<apex:column value= "{!c.status}" />
<apex:column value= "{!c.subject}" />
130
Advanced Examples Integrating Visualforce and Google Charts
This code shows the custom list controller associated with the page: public class retrieveCase { public String getContactName() { return 'Babara Levy' ;
} public List<Case> getCases() { return [SELECT status, subject FROM Case
WHERE Contact.name = 'Babara Levy' AND status != 'Closed' limit 5];
}
}
Sample of a Visualforce Page Running in a Dashboard
SEE ALSO:
Creating Visualforce Dashboard Components
Integrating Visualforce and Google Charts
Google Charts provides a way to dynamically render data through different visualizations. Combined with Visualforce, the Google Charts can offer more flexibility and distribution potential than using a dashboard. Since the charts are generated through a URL, the visualizations can be shared and embedded wherever images are permitted.
There are two prerequisites before using the Google Charts API. The first is to determine how to encode the data. The Google Charts API has three data encoding types—text, simple, and extended. For this example, we'll only use the simple encoding. The second is to decide what type of chart to use. For this example, a user will choose between a bar graph or a line chart.
The custom controller has two important functions— init()
and create()
—that correspond to the requirements above:
•
The function init()
takes a numeric value and converts it to Google Chart's simple data encoding type. For more information, see Simple Encoding Data Format in the Google Charts API documentation.
•
The function create()
constructs the URL that makes the request to the Google Charts API.
The following code represents the controller for the Visualforce page:
/* This class contains the encoding algorithm for use with the
Google chartAPI. */ public class GoogleDataEncoding {
// Exceptions to handle any erroneous data public class EncodingException extends Exception {} public class UnsupportedEncodingTypeException extends Exception {}
131
Advanced Examples Integrating Visualforce and Google Charts
/* The encoding map which takes an integer key and returns the respective encoding value as defined by Google.
This map is initialized in init() */ private Map< Integer , String > encodingMap { get; set; }
/* The maximum encoding value supported for the given encoding type. This value is set during init() */ private Integer encodingMax { get; set; }
/* The minimum encoding value supported for the given encoding type. This value is set during init() */ private Integer encodingMin { get; set; }
/* The encoding type according to Google's API. Only SIMPLE is implemented. */ public enum EncodingType { TEXT, SIMPLE, EXTENDED }
/* The minimum value to use in the generation of an encoding value. */ public Integer min { get; private set; }
/* The maximum value to use in the generation of an encoding value. */ public Integer max { get; private set; }
// The encoding type according to the API defined by Google public EncodingType eType { get; private set; }
// Corresponds to the data set provided by the page public String dataSet { get; set; }
// Corresponds to the type of graph selected on the page public String graph { get; set; }
// The URL that renders the Google Chart public String chartURL { get; set; }
// Indicates whether the chart should be displayed public Boolean displayChart { get; set; } public GoogleDataEncoding() { min = 0; max = 61; eType = EncodingType.SIMPLE; displayChart = false ; init();
} public PageReference create() {
String [] dataSetList = dataSet.split( ',' , 0);
String mappedValue = 'chd=s:' ; chartURL = 'http://chart.apis.google.com/chart?chs=600x300'
+ '&chtt=Time+vs|Distance&chxt=x,y,x,y'
132
Advanced Examples Integrating Visualforce and Google Charts
}
+ '&chxr=0,0,10,1|1,0,65,5'
+ '&chxl=2:|Seconds|3:|Meters' ; if (graph.compareTo( 'barChart' ) == 0)
{ chartURL += '&cht=bvs' ;
} else if (graph.compareTo( 'lineChart' ) == 0)
{ chartURL += '&cht=ls' ;
} else
{ throw new EncodingException( 'An unsupported chart type'
+ 'was selected: ' + graph + ' does not exist.' );
} for ( String dataPoint : dataSetList)
{ mappedValue += getEncode( Integer .valueOf(dataPoint.trim()));
} chartURL += '&' + mappedValue; displayChart = true ; return null ;
/* This method returns the encoding type parameter value that matches the specified encoding type. */ public static String getEncodingDescriptor(EncodingType t) { if (t == EncodingType.TEXT) return 't' ; else if (t == EncodingType.SIMPLE) return 's' ; else if (t == EncodingType.EXTENDED) return 'e' ; else return '' ;
}
/* This method takes a given number within the declared range of the encoding class and encodes it according to the encoding type. If the value provided fall outside of the declared range, an EncodingException is thrown. */ public String getEncode( Integer d) { if (d > max || d < min) { throw new EncodingException( 'Value provided ' + d
+ ' was outside the declared min/max range ('
+ min + '/' + max + ')' );
} else { return encodingMap.get(d);
}
}
/* This method initializes the encoding map which is then
133
Advanced Examples stored for expected repetitious use to minimize statement invocation. */ private void init() { if (eType == EncodingType.SIMPLE) { encodingMax = 61; encodingMin = 0; encodingMap = new Map< Integer , String >(); encodingMap.put(0, 'A' ); encodingMap.put(1, 'B' ); encodingMap.put(2, 'C' ); encodingMap.put(3, 'D' ); encodingMap.put(4, 'E' ); encodingMap.put(5, 'F' ); encodingMap.put(6, 'G' ); encodingMap.put(7, 'H' ); encodingMap.put(8, 'I' ); encodingMap.put(9, 'J' ); encodingMap.put(10, 'K' ); encodingMap.put(11, 'L' ); encodingMap.put(12, 'M' ); encodingMap.put(13, 'N' ); encodingMap.put(14, 'O' ); encodingMap.put(15, 'P' ); encodingMap.put(16, 'Q' ); encodingMap.put(17, 'R' ); encodingMap.put(18, 'S' ); encodingMap.put(19, 'T' ); encodingMap.put(20, 'U' ); encodingMap.put(21, 'V' ); encodingMap.put(22, 'W' ); encodingMap.put(23, 'X' ); encodingMap.put(24, 'Y' ); encodingMap.put(25, 'Z' ); encodingMap.put(26, 'a' ); encodingMap.put(27, 'b' ); encodingMap.put(28, 'c' ); encodingMap.put(29, 'd' ); encodingMap.put(30, 'e' ); encodingMap.put(31, 'f' ); encodingMap.put(32, 'g' ); encodingMap.put(33, 'h' ); encodingMap.put(34, 'i' ); encodingMap.put(35, 'j' ); encodingMap.put(36, 'k' ); encodingMap.put(37, 'l' ); encodingMap.put(38, 'm' ); encodingMap.put(39, 'n' ); encodingMap.put(40, 'o' ); encodingMap.put(41, 'p' ); encodingMap.put(42, 'q' ); encodingMap.put(43, 'r' ); encodingMap.put(44, 's' ); encodingMap.put(45, 't' ); encodingMap.put(46, 'u' );
Integrating Visualforce and Google Charts
134
Advanced Examples Integrating Visualforce and Google Charts encodingMap.put(47, 'v' ); encodingMap.put(48, 'w' ); encodingMap.put(49, 'x' ); encodingMap.put(50, 'y' ); encodingMap.put(51, 'z' ); encodingMap.put(52, '0' ); encodingMap.put(53, '1' ); encodingMap.put(54, '2' ); encodingMap.put(55, '3' ); encodingMap.put(56, '4' ); encodingMap.put(57, '5' ); encodingMap.put(58, '6' ); encodingMap.put(59, '7' ); encodingMap.put(60, '8' ); encodingMap.put(61, '9' );
}
}
}
The Visualforce page needs two input elements: one for the chart type, and one for the data set. Below is a sample page that constructs the form to collect this information:
<apex:page controller= "GoogleDataEncoding" >
<apex:form >
<apex:pageBlock title= "Create a Google Chart for Time and Distance" >
<apex:outputLabel value= "Enter data set, separated by commas: " for= "dataInput" /> <br/ >
<apex:inputTextArea id= "dataInput" title= "First Data Point" value= "{!dataSet}" rows= "3" cols= "50" /> <br/ >
<apex:selectRadio value= "{!graph}" layout= "pageDirection" >
<apex:selectOption itemValue= "barChart" itemLabel= "Horizontal Bar Chart" />
<apex:selectOption itemValue= "lineChart" itemLabel= "Line Chart" />
</apex:selectRadio >
<apex:commandButton action= "{!create}" value= "Create" />
</apex:pageBlock >
</apex:form >
<apex:image url= "{!chartURL}" alt= "Sample chart" rendered= "{!displayChart}" />
</apex:page >
For a sample, enter the following sequence of numbers:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55
. Your page should render the following:
135
Advanced Examples Mass-Updating Records with a Custom List Controller
Mass-Updating Records with a Custom List Controller
To create pages that perform mass updates, use the prototype object contained in the StandardSetController class.
The list controller tracks two sets of records: a primary list containing all the records selected by the filter, and a secondary list containing those records the user selected. The secondary list is usually established on a standard listview page where the user can check boxes to select the records. The user can then click on a custom list button that navigates to your custom mass update page, which uses the prototype object to apply new field values to the user's selection. The prototype object operates on all the records in the user's selection.
To retrieve the prototype object in your custom controller, use the StandardSetController's getRecord
method. For example, to enable mass updates for Opportunities, use the singular term for its associated object (
Opportunity
) to set field values for all records in the selection:
1. Create a Visualforce page called massupdatestages
.
2. Provide the following controller: public class selectedSizeWorkaround {
ApexPages.StandardSetController setCon; public selectedSizeWorkaround(ApexPages.StandardSetController controller) { setCon = controller;
} public integer getMySelectedSize() { return setCon.getSelected().size();
} public integer getMyRecordsSize() { return setCon.getRecords().size();
}
}
136
Advanced Examples Mass-Updating Records with a Custom List Controller
3. Provide the following markup:
<apex:page standardController= "Opportunity" recordSetVar= "opportunities" extensions= "selectedSizeWorkaround" showHeader= "false" id= "muopp"
>
<apex:form id= "muform" >
<apex:pageMessage summary= "Selected Collection Size: {!mySelectedSize}" severity= "info" id= "mupms"
/>
<apex:pageMessage summary= "Record Set Size: {!myRecordsSize}" severity= "info" id= "mupmr"
/>
<apex:pageBlock title= "Opportunity Mass-Update" mode= "edit" id= "mub1" >
<apex:pageMessages />
<apex:pageBlockSection id= "mus1" >
<apex:inputField value= "{!opportunity.stagename}" id= "stagename" >
<apex:actionSupport event= "onchange" rerender= "muselectedlist" />
</apex:inputField >
</apex:pageBlockSection >
<apex:pageBlockButtons location= "bottom" id= "mubut" >
<apex:commandButton value= "Save" action= "{!save}" id= "butsav" />
<apex:commandButton value= "Cancel" action= "{!cancel}" id= "butcan" />
</apex:pageBlockButtons >
</apex:pageBlock >
<apex:pageBlock title= "Selected Opportunities" id= "muselectedlist" >
<apex:pageBlockTable value= "{!selected}" var= "opp" id= "mutab" >
<apex:column value= "{!opp.name}" id= "oppname" />
<apex:column value= "{!opp.stagename}" id= "oppstage" />
</apex:pageBlockTable >
</apex:pageBlock >
</apex:form >
</apex:page >
4. From the object management settings for opportunities, go to Buttons, Links, and Actions.
5. Click New Button or Link.
6. Set the
Button Label
to
Mass Update Stages
, and set the
Name
to
MassUpdateStages
.
7. Set the
Display Type
to
List Button
and ensure that
Display Checkboxes (for Multi-Record
Selection)
is checked. Set the Behavior to
Display in existing window with sidebar
, and set the Content
Source to
Visualforce Page
. Click the name of the page you just created to associate it with this button.
8. Click Save.
9. From the object management settings for opportunities, go to Search Layouts. Then click Edit next to Opportunities List View.
10. Under Custom Buttons, move the Mass Update Stages button to the Selected Buttons list.
11. Click Save.
137
Advanced Examples Mass-Updating Records with a Custom List Controller
12. Click the Opportunities tab. Select or create a filter that displays some existing opportunities you would like to change.
13. You will see checkboxes next to each of the results. Click any number of checkboxes and click the Mass Update Stages button to change the selected stages to any value you wish.
14. Click Save.
While this example shows you how to update one field, any number of fields in the prototype object can be referenced and applied to the user's selection; any field in the prototype object that the user doesn't set doesn't affect the selected records. Remember that properties of fields, such as their requiredness, are maintained in the prototype object. For example, if you include an input field on the page for a required field such as
Opportunity.StageName
, the user must enter a value for the field.
Note: You only need selectedSizeWorkaround
if you want your page to either display or reference the sizes of the user selection or filtered set. Such a display is helpful since it gives the user information about the set that will be modified by the mass update.
SEE ALSO:
Salesforce Help: Find Object Management Settings
138
Download
Advertisement
Key features
Custom UI Development
Integration with Apex
PDF rendering
AJAX support
Custom components
Standard controller
Custom controller
Controller extension
Frequently asked questions
Visualforce is a framework that allows you to create custom user interfaces for Salesforce applications.
The document does not explicitly state which editions support Visualforce. See Salesforce official documentation for details.
The document explains some aspects of Visualforce architecture.