Best Practices. salesforce Visualforce Developer Guide
Publicité
Publicité
CHAPTER 22 Best Practices
The following best practices can be used in your Visualforce pages:
•
Best Practices for Improving Visualforce Performance
•
Best Practices for Accessing Component IDs
•
Best Practices for Static Resources
•
Best Practices for Controllers and Controller Extensions
•
Best Practices for Using Component Facets
•
Best Practices for Page Block Components
•
Best Practices for Rendering PDF Files
•
Best Practices for Improving Visualforce Performance
Visualforce was designed to provide developers with the ability to match the functionality, behavior, and performance of standard
Salesforce pages. If your users experience delays, unexpected behavior, or other issues specifically around Visualforce, there are several actions you can take to not only improve their experience, but to also make for improved coding.
First, determine whether Visualforce is the problem by ensuring that:
•
The problems aren’t confined to a single user’s computer by testing expected Visualforce functionality on other machines as well as using different browsers.
•
Slow load times aren’t the result of a network issue by checking the load time of other Salesforce pages. If they’re also slow, it could be the result of bandwidth or latency issues to Salesforce. To check on the status of the Salesforce servers, visit trust.salesforce.com
.
You should also check the status of your network connections and ensure they’re functioning properly.
•
You’re following general Web design best practices, such as the minification of JavaScript and CSS , optimizing images for the Web, and avoiding iframes whenever possible.
•
You’ve used the Developer Console to step through the request and determine which items in the request used the most system resources. See “Developer Console Functionality” in the Salesforce online help.
The following is a list of commonly encountered Visualforce performance issues and their possible solutions:
View State Size
The view state size of your Visualforce pages must be under 135 KB. By reducing your view state size, your pages can load quicker and stall less often.
•
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 that's relevant to the Visualforce page.
337
Best Practices Best Practices for Accessing Component IDs
•
If your view state is affected by a large component tree, try reducing the number of components your page depends on.
Load Times
Large page sizes directly affects load times. To improve Visualforce page load times:
•
Cache any data that is frequently accessed, such as icon graphics.
•
Avoid SOQL queries in your Apex controller getter methods.
•
Reduce the number of records displayed on a page by:
–
Limiting the data coming back from SOQL calls in your Apex controllers. For example, using
AND
statements in your
WHERE clause , or removing null
results
–
Taking advantage of
pagination with a list controller to present fewer records per page
•
“ Lazy load ” Apex objects to reduce request times.
•
Consider moving any JavaScript outside of the
<apex:includeScript >
tag and placing it into a
<script >
tag right before your closing
<apex:page >
tag. The
<apex:includeScript >
tag places JavaScript right before the closing
<head >
element; thus, Visualforce attempts to load the JavaScript before any other content on the page. However, you should only move JavaScript to the bottom of the page if you’re certain it doesn’t have any adverse effects to your page. For example,
JavaScript code snippets requiring document.write
or event handlers should remain in the
<head >
element.
In all cases, Visualforce pages must be under 15 MB.
Multiple Concurrent Requests
Concurrent requests are long-running tasks that could block other pending tasks. To reduce these delays:
•
Action methods used by
<apex:actionPoller >
should be lightweight. It’s a best practice to avoid performing DML, external service calls, and other resource-intensive operations in action methods called by an
<apex:actionPoller >
.
Carefully consider the effect of your action method being called repeatedly by an
<apex:actionPoller >
at the interval you specify, especially if it’s used on a page that will be widely distributed, or open continuously.
•
Increase the time interval for calling Apex from your Visualforce page. For example, when using the
<apex:actionPoller > component, you could adjust the interval
attribute to 30 seconds instead of 15.
•
Move non-essential logic to an asynchronous code block using Ajax.
Queries and Security
By using the with sharing
keyword when creating your Apex controllers, you have the possibility of
improving your SOQL queries by only viewing a data set for a single user .
Preventing Field Values from Dropping Off the Page
If your page contains many fields, including large text area fields, and has master-detail relationships with other entities, it may not display all data due to limits on the size of data returned to Visualforce pages and batch limits. The page displays this warning: “You requested too many fields to display. Consider removing some to prevent field values from being dropped from the display.”
To prevent field values from being dropped from the page, remove some fields to reduce the amount of data returned. Alternatively, you can write your own controller extensions to query child records to be displayed in the related lists.
Best Practices for Accessing Component IDs
To refer to a Visualforce component in JavaScript or another Web-enabled language, you must specify a value for the id
attribute for that component. A DOM ID is constructed from a combination of the id
attribute of the component and the id
attributes of all components that contain the element.
Use the
$Component
global variable to simplify referencing the DOM ID that is generated for a Visualforce component, and reduce some of the dependency on the overall page structure. To reference a specific Visualforce component’s DOM ID, add a component path specifier to
$Component
, using dot notation to separate each level in the component hierarchy of the page. For example, use
338
Best Practices Best Practices for Accessing Component IDs
$Component
.
itemId
to reference a component at the same level in the Visualforce component hierarchy, or use
$Component
.
grandparentId
.
parentId
.
itemId
to specify a more complete component path.
A
$Component
path specifier is matched against the component hierarchy:
•
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 you’re trying to match requires a traversal up and then back down, it won’t match.
The following example illustrates several uses of
$Component
:
<apex:page >
<style >
.clicker { border: 1px solid #999; cursor: pointer; margin: .5em; padding: 1em; width: 10em; text-align: center; }
</style >
<apex:form id= "theForm" >
<apex:pageBlock id= "thePageBlock" title= "Targeting IDs with $Component" >
<apex:pageBlockSection id= "theSection" >
<apex:pageBlockSectionItem id= "theSectionItem" >
All the alerts refer to this component.
<p >The full DOM ID resembles something like this: <br/ > j_id0:theForm:thePageBlock:theSection:theSectionItem </p >
</apex:pageBlockSectionItem >
<!-- Works because this outputPanel has a parent in common with "theSectionItem" component -->
<apex:outputPanel layout= "block" styleClass= "clicker" onclick= "alert('{!$Component.theSectionItem}');" >
First click here
</apex:outputPanel >
</apex:pageBlockSection >
<apex:pageBlockButtons id= "theButtons" location= "bottom" >
<!-- Works because this outputPanel has a grandparent ("theSection") in common with "theSectionItem" -->
<apex:outputPanel layout= "block" styleClass= "clicker" onclick= "alert('{!$Component.theSection.theSectionItem}');" >
Second click here
</apex:outputPanel >
<!-- Works because this outputPanel has a distant ancestor ("theForm") in common with "theSectionItem" -->
<apex:outputPanel layout= "block" styleClass= "clicker" onclick= "alert('
{!$Component.theForm.thePageBlock.theSection.theSectionItem} ');">
Third click here
</apex:outputPanel >
</apex:pageBlockButtons >
</apex:pageBlock >
339
Best Practices Best Practices for Accessing Component IDs
<!-- Works because this outputPanel is a sibling to "thePageBlock", and specifies the complete ID path from that sibling -->
<apex:outputPanel layout= "block" styleClass= "clicker" onclick= "alert('{!$Component.thePageBlock.theSection.theSectionItem}');" >
Fourth click here
</apex:outputPanel >
<hr/ >
<!-- Won't work because this outputPanel doesn't provide a path that includes a sibling or common ancestor -->
<apex:outputPanel layout= "block" styleClass= "clicker" onclick= "alert('{!$Component.theSection.theSectionItem}');" >
This won 't work
</apex:outputPanel >
<!-- Won't work because this outputPanel doesn't provide a path that includes a sibling or common ancestor -->
<apex:outputPanel layout= "block" styleClass= "clicker" onclick= "alert('{!$Component.theSectionItem}');" >
Won 't work either
</apex:outputPanel >
</apex:form >
</apex:page >
Using Unique IDs
Within each hierarchy segment in a page, the component id
must be unique. However, Salesforce recommends you use an id
that is unique on the page for every component you need to reference, and any components above it in the component hierarchy that are needed to reference it.
For example, suppose you had two data tables in a single page. If both data tables are contained in the same page block, they must have unique id
attributes. If each is contained in a separate page block, it’s possible to give them the same component id
. If you do so, however, the only way to reference a specific data table is to assign an id
to every component and then reference the data table component using the complete hierarchy, rather than letting Visualforce do it automatically. If the page hierarchy ever changes, your program will no longer work.
Iterating with Component IDs
Some components, such as tables and lists, support iteration over a collection of records. After you assign an ID for these types of components, the system assigns a unique “compound ID” to each iteration of the component based on the initial ID.
For example, the following page contains a data table with an ID set to theTable
.
<apex:page standardController= "Account" recordSetVar= "accounts" id= "thePage" >
<apex:dataTable value= "{!accounts}" var= "account" id= "theTable" >
<apex:column id= "firstColumn" >
<apex:outputText value= "{!account.name}" />
</apex:column >
<apex:column id= "secondColumn" >
<apex:outputText value= "{!account.owner.name}" />
340
Best Practices Best Practices for Static Resources
</apex:column >
</apex:dataTable >
</apex:page >
When the page is rendered, the
<apex:dataTable >
component results in the following HTML:
<table id= "thePage:theTable" border= "0" cellpadding= "0" cellspacing= "0" >
<colgroup span= "2" />
<tbody >
<tr class= "" >
<td id= "thePage:theTable:0:firstColumn" >
<span id= "thePage:theTable:0:accountName" >Burlington Textiles </span >
</td >
<td id= "thePage:theTable:0:secondColumn" >
<span id= "thePage:theTable:0:accountOwner" >Vforce Developer </span >
</td >
</tr >
<tr class= "" >
<td id= "thePage:theTable:1:firstColumn" >
<span id= "thePage:theTable:1:accountName" >Dickenson </span >
</td >
<td id= "thePage:theTable:1:secondColumn" >
</tr >
</table >
<span id= "thePage:theTable:1:accountOwner" >Vforce Developer </span >
</td >
Each table cell has a unique ID based on the ID value of the containing components. The first table cell in the first row has the ID thePage:theTable:0:firstColumn
, the second cell in the first row has the ID thePage:theTable:0:secondColumn
, the first cell in the second row has the ID thePage:theTable:1:firstColumn
, and so on.
To refer to all entries in a column, you have to iterate across the table rows, referring to each
<td >
element that has an ID following the format of the column.
The same type of ID generation is done for elements within the table cells. For example, the account name in the first row is generated as a span
with the ID thePage:theTable:0:accountName
. Notice that ID does not include the value of the ID for the column it’s in.
Best Practices for Static Resources
Displaying the Content of a Static Resource with the action
Attribute on
<apex:page >
You can use the action
attribute on a
<apex:page >
component to redirect from a Visualforce page to a static resource. This functionality allows you to add rich, custom help to your Visualforce pages. For example, to redirect a user to a PDF:
1. Upload the PDF as a static resource named customhelp
.
2. Create the following page:
<apex:page sidebar= "false" showHeader= "false" standardStylesheets= "false" action= "{!URLFOR($Resource.customhelp)}" >
</apex:page >
Notice that the static resource reference is wrapped in a
URLFOR
function. Without that, the page does not redirect properly.
341
Best Practices Best Practices for Controllers and Controller Extensions
This redirect is not limited to PDF files. You can also redirect a page to the content of any static resource. For example, you can create a static resource that includes an entire help system composed of many HTML files mixed with JavaScript, images, and other multimedia files. As long as there is a single entry point, the redirect works. For example:
1. Create a zip file that includes your help content.
2. Upload the zip file as a static resource named customhelpsystem
.
3. Create the following page:
<apex:page sidebar= "false" showHeader= "false" standardStylesheets= "false" action= "{!URLFOR($Resource.customhelpsystem, 'index.htm')}" >
</apex:page >
When a user visits the page, the index.htm
file in the static resource displays.
SEE ALSO:
Best Practices for Controllers and Controller Extensions
Enforcing Sharing Rules in Controllers
Like other Apex classes, custom controllers and controller extensions run in system mode.
Typically, you want a controller or controller extension to respect a user’s organization-wide defaults, role hierarchy, and sharing rules. You can do that by using the with sharing
keywords in the class definition. For information, see “Using the with sharing
or without sharing
Keywords” in the
Apex Developer Guide
.
Note: If a controller extension extends a standard controller, the logic from the standard controller doesn’t execute in system mode. Instead, it executes in user mode, in which the permissions, field-level security, and sharing rules of the current user apply.
Controller Constructors Evaluate Before Setter Methods
Do not depend on a setter method being evaluated before a constructor. For example, in the following component, the component's controller depends on the setter for selectedValue
being called before the constructor method:
<apex:component controller= "CustCmpCtrl" >
<apex:attribute name= "value" description= ""
</apex:attribute >
//...
type= "String" required= "true" assignTo= "{!selectedValue}" >
//...
</apex:component > public class CustCmpCtrl {
// Constructor method public CustCmpCtrl() { if (selectedValue != null) {
EditMode = true;
}
}
342
Best Practices Best Practices for Using Component Facets private Boolean EditMode = false;
// Setter method public String selectedValue { get;set; }
}
Since the constructor is called before the setter, selectedValue
will always be null when the constructor is called. Thus,
EditMode
will never be set to true.
Methods may evaluate more than once — do not use side-effects
Methods, including methods in a controller, action attributes, and expressions, may be called more than once. Do not depend on evaluation order or side-effects when creating custom methods in a controller or controller extension.
Best Practices for Using Component Facets
A facet consists of content in an area of a Visualforce component that provides contextual information about the data that is presented in the component. For example,
<apex:dataTable >
supports facets for the header, footer, and caption of a table, while
<apex:column >
only supports facets for the header and footer of the column. The
<apex:facet >
component allows you to override the default facet on a Visualforce component with your own content. Facets only allow a single child within the start and close tags.
Note: Not all components support facets. Those that do are listed in the
.
When defining an
<apex:facet >
, it is always used as the child of another Visualforce component. The name
attribute on the facet determines which area of the parent component is overridden.
Example: Using Facets with
<apex:dataTable >
The following markup shows how the
<apex:dataTable >
component can be modified with
<apex:facet >
:
<apex:page standardController= "Account" >
<apex:pageBlock >
<apex:dataTable value= "{!account}" var= "a" >
<apex:facet name= "caption" > <h1 >This is
{!account.name} </h1 > </apex:facet >
<apex:facet name= "footer" > <p >Information
Accurate as of {!NOW()} </p > </apex:facet >
<apex:column >
<apex:facet name= "header" >Name </apex:facet >
<apex:outputText value= "{!a.name}" />
</apex:column >
<apex:column >
<apex:facet name= "header" >Owner </apex:facet >
<apex:outputText value= "{!a.owner.name}" />
</apex:column >
</apex:dataTable >
</apex:pageBlock >
</apex:page >
343
Best Practices Best Practices for Using Component Facets
Note: 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/facet?id=001D000000IRosz
The page displays as follows:
Extending
<apex:dataTable >
with a Facet
Using Facets with
<apex:actionStatus >
Another component that can use a facet is
<apex:actionStatus >
. The
<apex:actionStatus >
component can be extended to display an indicator whenever a page is being refreshed. For example, you can define a progress wheel with the following markup:
<apex:page controller= "exampleCon" >
<apex:form >
<apex:outputText value= "Watch this counter: {!count}" id= "counter" />
<apex:actionStatus id= "counterStatus" >
<apex:facet name= "start" >
<img src= "{!$Resource.spin}" /> <!-- A previously defined image -->
</apex:facet >
</apex:actionStatus >
<apex:actionPoller action= "{!incrementCounter}" rerender= "counter" status= "counterStatus" interval= "7" />
</apex:form >
</apex:page >
The associated controller updates the counter: public class exampleCon {
Integer count = 0; public PageReference incrementCounter() { count++; return null ;
} public Integer getCount() { return count;
}
}
The page displays as follows:
344
Best Practices
Extending
<apex:actionStatus >
with a Facet
Best Practices for Page Block Components
SEE ALSO:
Best Practices for Page Block Components
Adding More than Two Child Components to
<apex:pageBlockSectionItem >
An
<apex:pageBlockSectionItem >
component can only have up to two child components. Sometimes, though, you want to add an extra child component. For example, you may want to add an asterisk before an
<apex:outputLabel >
and still display the associated input text field. You can do this by wrapping the asterisk and output label in an
<apex:outputPanel > component, as follows:
Note: 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=001D000000IRosz
<!-- Page: -->
<apex:page standardController= "Account" >
<apex:form >
<apex:pageBlock title= "My Content" mode= "edit" >
<apex:pageBlockSection title= "My Content Section" columns= "2" >
<apex:pageBlockSectionItem >
<apex:outputPanel >
</apex:pageBlockSectionItem >
</apex:pageBlockSection >
</apex:pageBlock >
</apex:form >
</apex:page >
<apex:outputText >* </apex:outputText >
<apex:outputLabel value= "Account Name" for= "account__name" />
</apex:outputPanel >
<apex:inputText value= "{!account.name}" id= "account__name" />
Best Practices for Rendering PDF Files
Rendering a Visualforce page as a PDF file is a great way to share information about your Salesforce organization. Here are some best practices for you to consider.
For better performance when rendering Visualforce pages, reference static image and style sheet resources through the
.
Warning: Referencing static resources on a remote server increases the time it takes to render a Visualforce page as a PDF file.
Add remote servers to your permitted Remote Sites list: From Setup, enter
Remote Sites Settings
in the
Quick Find
345
Best Practices Best Practices for <apex:panelbar> box, then select Remote Sites Settings. You can’t reference remote resources when using Visualforce to render PDF files in an
Apex trigger. Doing so results in an exception.
SEE ALSO:
Render a Visualforce Page as a PDF File
Visualforce PDF Rendering Considerations and Limitations
Best Practices for
<apex:panelbar >
Adding a Collection of Child
<apex:panelBarItem >
Components to an
<apex:panelBar >
Component
An
<apex:panelBar >
component can only have
<apex:panelBarItem >
child components. Sometimes, though, you want to add a collection of child components. For example, you may want to add an item for each contact associated with an account. You can do this by wrapping
<apex:panelBarItem >
in an
<apex:repeat >
component, as follows:
Note: 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=001D000000IRosz
<apex:page standardController= "account" >
<apex:panelBar >
<apex:repeat value= "{!account.contacts}" var= "c" >
<apex:panelBarItem label= "{!c.firstname}" >one </apex:panelBarItem >
</apex:repeat >
</apex:panelBar >
</apex:page >
346
Télécharger
Publicité