标准控制器可以提供 Visualforce 页面所需的所有功能 因为它们包含用于标准页面的相同逻辑。例如,如果你 使用标准帐户控制器,单击 Visualforce 页面产生的行为与在 标准帐户编辑页面。但是,如果要覆盖现有功能,请通过以下方式自定义导航 应用程序,使用标注或 Web 服务,或者如果您需要更精细地控制方式 访问页面的信息,您可以编写自定义控制器或控制器 使用 Apex 的扩展:
- 什么是自定义 控制器和控制器扩展?
- 构建自定义 控制器
- 构建一个 控制器扩展
- 控制器 方法
- Controller 类 安全
- 考虑 用于创建自定义控制器和控制器扩展
- 执行顺序 在 Visualforce 页面中
- 测试自定义 控制器和控制器扩展
- 验证规则 和自定义控制器
- 使用 transient 关键字
什么是自定义控制器和控制器扩展?
自定义控制器是一个 Apex 类,用于实现页面的所有逻辑 无需使用标准控制器。当您希望 Visualforce 页面完全在系统中运行时,请使用自定义控制器 模式,该模式不强制执行当前用户的权限和字段级安全性。控制器扩展是一个 Apex 类,它扩展了 标准或自定义控制器。在以下情况下使用控制器扩展:
- 您希望利用标准控制器的内置功能,但要覆盖一个或 更多操作,例如编辑、查看、保存或删除。
- 您想要添加新操作。
- 您想要构建 Visualforce 页面 尊重用户权限。尽管控制器扩展类在系统模式下执行, 如果控制器扩展扩展标准控制器,则来自标准控制器的逻辑 不在系统模式下执行。相反,它在用户模式下执行,其中权限, 字段级安全性,并应用当前用户的共享规则。
注意
自定义控制器和控制器扩展类在 系统模式,因此它们会忽略用户权限和字段级安全性。但是,您可以 选择是否遵循用户在组织范围内的默认值、角色层次结构和共享 规则,方法是使用 类定义。有关信息,请参阅《使用 、 和关键字》中的“使用 Apex 开发人员 指南。with sharingwith sharingwithout sharinginherited sharing
构建自定义控制器
自定义控制器是使用默认的无参数构造函数的 Apex 类 对于外部的顶级类。不能创建 包括参数。
- 在“设置”中,在“快速”中输入 Apex 类 “查找”框,然后选择“Apex 类”。
- 单击“新建”。
- 单击“版本设置”以指定 Apex 的版本 以及与此类一起使用的 API。如果您的组织已安装托管 软件包,您还可以指定每个软件包的版本 用于此类的托管包。对所有版本使用默认值。 这会将该类与最新版本的 Apex 和 API 相关联,如下所示 以及每个托管包。您可以指定旧版本的托管 软件包,如果您要访问与 最新的包版本。您可以指定旧版本的 Apex 和 用于维护特定行为的 API。
- 在类编辑器中,输入类的 Apex 代码。单个类可以是 长度不超过 100 万个字符,不包括注释、测试方法或 使用 @isTest 定义的类。
- 单击“保存”以保存更改并返回到课程 详细信息屏幕,或单击“快速保存”以保存更改 ,然后继续编辑您的课程。您的 Apex 类必须正确编译才能 您可以保存您的课程。
例
下面的类是自定义控制器的简单示例:
public class MyController {
private final Account account;
public MyController() {
account = [SELECT Id, Name, Site FROM Account
WHERE Id = :ApexPages.currentPage().getParameters().get('id')];
}
public Account getAccount() {
return account;
}
public PageReference save() {
update account;
return null;
}
}
以下 Visualforce 标记显示了如何在 一页:
<apex:page controller="myController" tabStyle="Account">
<apex:form>
<apex:pageBlock title="Congratulations {!$User.FirstName}">
You belong to Account Name: <apex:inputField value="{!account.name}"/>
<apex:commandButton action="{!save}" value="save"/>
</apex:pageBlock>
</apex:form>
</apex:page>
由于组件的属性,自定义控制器与页面相关联。controller<apex:page>
与标准控制器和控制器扩展一样,自定义控制器方法可以 用符号引用 关联的页面标记。在上面的示例中,该方法由标记的属性引用,而标记使用其属性引用该方法。{! }getAccount<apex:inputField>value<apex:commandButton>saveaction
注意
与其他 Apex 类一样,所有自定义控制器都在系统模式下运行。因此,当前用户的凭据不用于 执行控制器逻辑,用户权限和字段级安全性执行 不适用。
您可以选择自定义控制器是否遵循用户的 使用类中的关键字的组织范围的默认值、角色层次结构和共享规则 定义。with sharing有关信息,请参阅《使用 、 和关键字》中的“使用 Apex 开发人员 指南。with sharingwithout sharinginherited sharing
自定义控制器还可用于 创建新记录。为 例:
public class NewAndExistingController {
public Account account { get; private set; }
public NewAndExistingController() {
Id id = ApexPages.currentPage().getParameters().get('id');
account = (id == null) ? new Account() :
[SELECT Name, Phone, Industry FROM Account WHERE Id = :id];
}
public PageReference save() {
try {
upsert(account);
} catch(System.DMLException e) {
ApexPages.addMessages(e);
return null;
}
// After successful Save, navigate to the default view page
PageReference redirectSuccess = new ApexPages.StandardController(Account).view();
return (redirectSuccess);
}
}
以下 Visualforce 标记显示了如何在 一页:
<apex:page controller="NewAndExistingController" tabstyle="Account">
<apex:form>
<apex:pageBlock mode="edit">
<apex:pageMessages/>
<apex:pageBlockSection>
<apex:inputField value="{!account.name}"/>
<apex:inputField value="{!account.phone}"/>
<apex:inputField value="{!account.industry}"/>
</apex:pageBlockSection>
<apex:pageBlockButtons location="bottom">
<apex:commandButton value="Save" action="{!save}"/>
</apex:pageBlockButtons>
</apex:pageBlock>
</apex:form>
</apex:page>
构建控制器扩展
控制器扩展是任何 Apex 类,其中包含一个构造函数,该构造函数采用单个 类型为 或 的参数,其中 是所需的自定义控制器的名称 以扩展。ApexPages.StandardControllerCustomControllerNameCustomControllerName
下面的类是控制器扩展的简单示例:
public class myControllerExtension {
private final Account acct;
// The extension constructor initializes the private member
// variable acct by using the getRecord method from the standard
// controller.
public myControllerExtension(ApexPages.StandardController stdController) {
this.acct = (Account)stdController.getRecord();
}
public String getGreeting() {
return 'Hello ' + acct.name + ' (' + acct.id + ')';
}
}
以下 Visualforce 标记 显示了如何在页面中使用上面的控制器扩展:
<apex:page standardController="Account" extensions="myControllerExtension">
{!greeting} <p/>
<apex:form>
<apex:inputField value="{!account.name}"/> <p/>
<apex:commandButton value="Save" action="{!save}"/>
</apex:form>
</apex:page>
扩展使用组件的属性与页面相关联。extensions<apex:page>
与所有控制器方法一样,控制器扩展方法可以在页面标记中使用表示法进行引用。在 上面的例子,表达式 页面顶部引用了控制器扩展的方法。{! }{!greeting}getGreeting
由于此扩展与 Account 标准控制器结合使用,因此 还提供标准控制器方法。例如,标记中的属性检索 使用标准控制器功能的帐户。同样,标签引用标准 account 方法及其属性。value<apex:inputField><apex:commandButton>saveaction可以通过逗号分隔为单个页面定义多个控制器扩展 列表。这允许重写具有相同名称的方法。例如,如果 以下页面 存在:
<apex:page standardController="Account"
extensions="ExtOne,ExtTwo" showHeader="false">
<apex:outputText value="{!foo}" />
</apex:page>
跟 以下 扩展:
public class ExtOne {
public ExtOne(ApexPages.StandardController acon) { }
public String getFoo() {
return 'foo-One';
}
}
public class ExtTwo {
public ExtTwo(ApexPages.StandardController acon) { }
public String getFoo() {
return 'foo-Two';
}
}
这 组件的值 呈现为 .覆盖由以下任一因素定义 方法在“最左边”扩展中定义,或者在 在逗号分隔列表中的第一个。因此,的方法覆盖了 的方法。
<apex:outputText>foo-OnegetFooExtOneExtTwo
注意
与其他 Apex 类一样,控制器扩展在 系统模式。因此,当前用户的凭据不用于 执行控制器逻辑,用户权限和字段级安全性执行 不适用。然而 如果控制器扩展扩展标准控制器,则来自标准控制器的逻辑 控制器不在系统模式下执行。相反,它在用户模式下执行,其中 当前用户的权限、字段级安全性和共享规则 应用。
您可以选择控制器扩展是否遵循用户的 使用类定义中的关键字实现组织范围的默认值、角色层次结构和共享规则。 有关信息,请参阅《使用 、 和关键字》中的“使用 Apex 开发人员 指南。with sharingwith sharingwithout sharinginherited sharing
构建自定义列表控制器
自定义列表控制器类似于标准列表控制器。自定义列表 控制器可以实现您定义的 Apex 逻辑,以显示或操作一组 记录。例如,您可以创建以下自定义列表控制器,基于 一个 SOQL 查询:
public class opportunityList2Con {
// ApexPages.StandardSetController must be instantiated
// for standard list controllers
public ApexPages.StandardSetController setCon {
get {
if(setCon == null) {
setCon = new ApexPages.StandardSetController(Database.getQueryLocator(
[SELECT Name, CloseDate FROM Opportunity]));
}
return setCon;
}
set;
}
// Initialize setCon and return a list of records
public List<Opportunity> getOpportunities() {
return (List<Opportunity>) setCon.getRecords();
}
}
注意
这 返回的 sObject 列表是 变。例如,你不能调用它。您可以对 列表,但无法向列表添加项目或从列表中删除项目 本身。getRecords()clear()以下 Visualforce 标记 展示了如何在 页:
<apex:page controller="opportunityList2Con">
<apex:pageBlock>
<apex:pageBlockTable value="{!opportunities}" var="o">
<apex:column value="{!o.Name}"/>
<apex:column value="{!o.CloseDate}"/>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:page>
您还可以创建一个自定义列表控制器,该控制器使用反联接和半联接作为 SOQL 查询。以下代码是作为帐户的扩展实现的 标准 控制器:
public with sharing class AccountPagination {
private final Account acct;
// The constructor passes in the standard controller defined
// in the markup below
public AccountPagination(ApexPages.StandardSetController controller) {
this.acct = (Account)controller.getRecord();
}
public ApexPages.StandardSetController accountRecords {
get {
if(accountRecords == null) {
accountRecords = new ApexPages.StandardSetController(
Database.getQueryLocator([SELECT Name FROM Account WHERE Id NOT IN
(SELECT AccountId FROM Opportunity WHERE IsClosed = true)]));
}
return accountRecords;
}
private set;
}
public List<Account> getAccountPagination() {
return (List<Account>) accountRecords.getRecords();
}
}
这 显示这些记录的页面混合使用标准列表控制器操作,但 依赖于循环访问从自定义列表返回的记录 控制器:
<apex:page standardController="Account" recordSetVar="accounts" extensions="AccountPagination">
<apex:pageBlock title="Viewing Accounts">
<apex:form id="theForm">
<apex:pageBlockSection >
<apex:dataList value="{!accountPagination}" var="acct" type="1">
{!acct.name}
</apex:dataList>
</apex:pageBlockSection>
<apex:panelGrid columns="2">
<apex:commandLink action="{!previous}">Previous</apex:commandlink>
<apex:commandLink action="{!next}">Next</apex:commandlink>
</apex:panelGrid>
</apex:form>
</apex:pageBlock>
</apex:page>
控制器方法
Visualforce 标记可以使用 以下类型的控制器扩展和自定义控制器方法:
- 行动
- 吸气剂
- 二传手
操作方法
操作方法在页面执行逻辑或导航时 事件发生,例如当用户单击按钮或将鼠标悬停在 页。可以通过在以下参数之一的参数中使用表示法从页面标记中调用操作方法 标签:{! }action
- <apex:commandButton> 创建一个按钮,该按钮调用 行动
- <apex:commandLink> 创建一个调用操作的链接
- <apex:actionPoller> 定期调用操作
- <apex:actionSupport> 创建一个事件(例如 “onclick”、“onmouseover”等) 命名组件,调用操作
- <apex:actionFunction> 定义了一个新的 JavaScript 调用操作的函数
- <apex:page> 调用操作 加载页面时
例如,在“生成自定义控制器”的示例页中,控制器的方法 由标记的参数调用。其他 定义操作方法中讨论了操作方法的示例。saveaction<apex:commandButton>
Getter 方法
Getter 方法从控制器返回值。每个值 由控制器计算并显示在页面中必须具有相应的 getter 方法,包括任何布尔变量。例如,在生成自定义控制器的示例页中,控制器包含一个方法。此方法允许页面标记引用 带有表示法的控制器类。标记的参数使用此表示法来 访问帐户,并使用点表示法显示帐户的名称。Getter 方法 必须始终命名为 。getAccountaccount{! }value<apex:inputField>getVariable
重要
它 getter 方法的最佳实践是幂等的,即没有 副作用。例如,不要递增变量,写日志消息, 或将新记录添加到数据库。Visualforce 没有 定义调用 getter 方法的顺序,或者调用它们的次数 在处理请求的过程中调用。设计 getter 方法以产生 相同的结果,无论它们被调用一次还是多次用于单个页面 请求。
Setter 方法
Setter 方法将用户指定的值从页面标记传递给 控制器。控制器中的任何 setter 方法都会在 任何操作方法。
例如,以下标记显示一个页面 实现 Leads 的基本搜索功能。关联的控制器包括 getter 和 setter 方法,然后使用搜索文本来 当用户单击 Go! 时发出 SOSL 查询。虽然 标记不会显式调用搜索文本 setter 方法,它会执行 在操作方法之前,当 用户单击命令 按钮:doSearch
<apex:page controller="theController">
<apex:form>
<apex:pageBlock mode="edit" id="block">
<apex:pageBlockSection>
<apex:pageBlockSectionItem>
<apex:outputLabel for="searchText">Search Text</apex:outputLabel>
<apex:panelGroup>
<apex:inputText id="searchText" value="{!searchText}"/>
<apex:commandButton value="Go!" action="{!doSearch}"
rerender="block" status="status"/>
</apex:panelGroup>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
<apex:actionStatus id="status" startText="requesting..."/>
<apex:pageBlockSection title="Results" id="results" columns="1">
<apex:pageBlockTable value="{!results}" var="l"
rendered="{!NOT(ISNULL(results))}">
<apex:column value="{!l.name}"/>
<apex:column value="{!l.email}"/>
<apex:column value="{!l.phone}"/>
</apex:pageBlockTable>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
这 以下类是页面标记的控制器 以上:
public class theController {
String searchText;
List<Lead> results;
public String getSearchText() {
return searchText;
}
public void setSearchText(String s) {
searchText = s;
}
public List<Lead> getResults() {
return results;
}
public PageReference doSearch() {
results = (List<Lead>)[FIND :searchText RETURNING Lead(Name, Email, Phone)][0];
return null;
}
}
而 从控制器访问值始终需要 getter 方法,它是 并不总是需要包含 setter 方法以将值传递到控制器中。如果 Visualforce 组件是 绑定到存储在控制器中的 sObject,sObject 的字段为 如果用户更改,则自动设置,只要保存或更新 sObject 即可 通过相应的动作方法。示例中显示了此行为的示例 页面。
Setter 方法必须始终命名为 。setVariable
重要
最佳做法是 setter 方法是幂等的,即没有副作用。例如,不要 递增变量、写入日志消息或向数据库添加新记录。Visualforce 没有 定义调用 setter 方法的顺序,或者调用它们的次数 在处理请求的过程中调用。设计 setter 方法以产生 相同的结果,无论它们被调用一次还是多次用于单个页面 请求。
使用自定义扩展获取和设置数据,或者 控制器
有 无法保证 Apex 方法和变量的处理顺序 控制器扩展或自定义控制器。因此,不允许控制器和 扩展类依赖于正在运行的另一个方法,请直接调用该方法。 这特别适用于设置变量和从数据库访问数据。
例如,在以下自定义控制器中,第一个方法 , 始终返回正确的 值,因为它不假定 contact 变量已存在。但是,第二种方法有时会返回 正确的值,但不是每次都正确 设置。getContactMethod1cgetContactMethod2c
public class conVsBad {
Contact c;
public Contact getContactMethod1() {
if (c == null) c = [SELECT Id, Name FROM Contact LIMIT 1];
return c;
}
public Contact getContactMethod2() {
return c;
}
}
这 以下自定义控制器具有完全相同的方法。但是,调用 ,因此变量始终被设置,并且始终包含 返回时的值正确。getContactMethod2contactMethod1c
public class conVsGood {
Contact c;
public Contact getContactMethod1() {
if(c == null) c = [SELECT Id, Name FROM Contact LIMIT 1];
return c;
}
public Contact getContactMethod2() {
return getContactMethod1();
}
}
这 以下标记显示调用这些控制器的两个页面。Visualforce 标记是 相同,只有控制器名称是 改变:
<apex:page controller="conVsGood">
getContactMethod2(): {!contactMethod2.name}<br/>
getContactMethod1(): {!contactMethod1.name}
</apex:page>
<apex:page controller="conVsBad">
getContactMethod2(): {!contactMethod2.name}<br/>
getContactMethod1(): {!contactMethod1.name}
</apex:page>
控制器类安全性
与其他 Apex 类一样,您可以指定用户是否可以在自定义中执行方法 控制器或基于用户配置文件的控制器扩展类。
注意
如果您在组织中安装了托管软件包,则只能为 Apex 设置安全性 包中声明为 或 的类 对于包含声明为 的方法的类。globalwebService
如果用户具有 Author Apex 权限,则可以访问所有 Apex 关联组织中的类,而不考虑个人的安全设置 类。
仅在顶层检查 Apex 类的权限。例如,类 A 调用类 B. 用户 X 有一个可以访问 A 类但不能访问 B 类的配置文件。用户 X 可以在 B类,但只能通过A类;用户 X 不能直接调用类 B。同样,如果 Visualforce 页面使用带有关联控制器的自定义组件,仅检查安全性 用于与页面关联的控制器。与自定义组件关联的控制器 无论权限如何,都会执行。
要从类列表页面设置 Apex 类安全性,请执行以下操作:
从类列表中设置 Apex 类访问权限 页
要从类详细信息页面设置 Apex 类安全性,请执行以下操作: 从类详细信息设置 Apex 类访问权限 页
处理大型数据集
Visualforce 自定义控制器和 控制器扩展受 Apex 调控器限制的约束。有关调速器的详细信息 限制,请参阅执行调控器和限制。此外,Visualforce 迭代组件(如 和)限制为 中最多 1,000 个项目 他们迭代的集合。<apex:pageBlockTable><apex:repeat>
有时,您的 Visualforce 页面可能会 需要处理或显示更大的数据集,但不需要对其进行修改 数据;例如,如果您要提供自定义报告和分析。Visualforce 为开发人员提供了”只读模式“,放宽了对数量的限制 可以在一个请求中查询的行,并增加对收集数量的限制 可以在页面内迭代的项目。您可以为整个页面指定只读模式,也可以在有某些限制的情况下指定只读模式 单个组件或方法。
注意
只有在以下情况下,才能循环访问大型数据集 为整个页面指定只读模式。
为整个页面设置只读模式
要为整个页面启用只读模式,请将组件上的属性设置为 。
readOnly<apex:page>true
例如,下面是一个以只读模式处理的简单页面:
<apex:page controller="SummaryStatsController" readOnly="true">
<p>Here is a statistic: {!veryLargeSummaryStat}</p>
</apex:page>
此页面的控制器也很简单,但说明了如何 您可以计算在页面上显示的汇总统计信息:
public class SummaryStatsController {
public Integer getVeryLargeSummaryStat() {
Integer closedOpportunityStats =
[SELECT COUNT() FROM Opportunity WHERE Opportunity.IsClosed = true];
return closedOpportunityStats;
}
}
通常,对单个 Visualforce 页面请求的查询检索的行数可能不会超过 50,000 行。在只读模式下,此限制已放宽,最多允许查询 1,000,000 行。
除了查询更多行之外,该属性还增加了集合中可以 使用 、 和 等组件进行迭代。此限制从 1,000 增加到 项目到 10,000。下面是一个简单的控制器和页面演示:readOnly<apex:dataTable><apex:dataList><apex:repeat>
public class MerchandiseController {
public List<Merchandise__c> getAllMerchandise() {
List<Merchandise__c> theMerchandise =
[SELECT Name, Price__c FROM Merchandise__c LIMIT 10000];
return(theMerchandise);
}
}
<apex:page controller="MerchandiseController" readOnly="true">
<p>Here is all the merchandise we have:</p>
<apex:dataTable value="{!AllMerchandise}" var="product">
<apex:column>
<apex:facet name="header">Product</apex:facet>
<apex:outputText value="{!product.Name}" />
</apex:column>
<apex:column>
<apex:facet name="header">Price</apex:facet>
<apex:outputText value="{!product.Price__c}" />
</apex:column>
</apex:dataTable>
</apex:page>
虽然对整个页面使用只读模式的 Visualforce 页面不能使用数据操作语言 (DML) 操作, 它们可以调用影响表单和其他用户的 getter、setter 和 action 方法 界面元素,进行其他只读查询,依此类推。
为控制器方法设置只读模式
Visualforce 控制器方法 可以使用 Apex 注释,但有一些重要的限制,即使页面本身不是只读模式。
ReadOnly带有注释的 Visualforce 控制器方法会自动利用只读模式。 但是,对注释的限制意味着 对于 Visualforce 控制器方法, 只读方法也必须具有批注。注解要求 方法是:
@ReadOnly@ReadOnly@RemoteAction@RemoteAction
- Either 或globalpublic
- static
启用只读模式 必须在顶部使用注释 level 方法调用。如果顶级方法调用没有注解,则对最大查询量的正常限制 对整个请求强制执行行,即使辅助方法使用 注释了 。@ReadOnly@ReadOnly@ReadOnly
在控制器方法上使用批注 允许您检索更大的记录集合作为 Visualforce 表达式的结果。但是,它 不会增加迭代组件的集合中的最大项数。如果 想要遍历更大的结果集合,需要为 整个页面。@ReadOnly
创建自定义控制器和控制器的注意事项 扩展
创建控制器扩展和自定义控制器时,请注意以下注意事项:
- 除非一个类具有定义为 的方法,否则自定义扩展和控制器类和方法通常 定义为 。如果类包含 Web 服务方法,则必须将其定义为 。webServicepublicglobal
- 从数据库返回数据时,请使用集、映射或列表。这将使您的代码 效率更高,因为代码对数据库的访问更少。
- Visualforce 控制器扩展的 Apex 调控器限制和 自定义控制器与匿名块或 WSDL 方法的限制相同。为 有关调控器限制的详细信息,请参阅附录中的执行调控器和限制。
- 如果要构建自定义控制器或控制器扩展,请注意这样做 不会无意中暴露通常对用户隐藏的敏感数据。考虑 在类上使用关键字 用于强制执行权限的定义。另外,使用受保护的 Web 服务时也要小心 作为配置文件的顶级入口点,但一旦它们被执行,就会在系统上下文中执行 发起。with sharing
- Apex 方法和变量不按保证顺序实例化。查看更多 信息,请参阅使用自定义扩展获取和设置数据或 控制器。
- 不能在“getxxx”中使用数据操作语言 (DML) 操作 方法。例如,如果控制器具有方法,则不能在该方法中使用 或 创建对象。getNameinsertupdate
- 不能在构造函数方法中使用数据操作语言 (DML) 操作 控制器。
- 您不能在 “getxxx”或“setxxx”方法,或者在 控制器的构造函数。@future
- Primitive Apex 数据类型(如 String 或 Integer)按值传递给 组件的控制器。
- 非原始 Apex 数据类型(如 lists 和 sObjects)通过引用传递给 组件的控制器。这意味着,如果组件的控制器更改了 帐户,更改可在页面的控制器中找到。
- 如果您的组织使用个人帐户
- 使用自定义引用客户记录的名称字段时 控制器使用您必须在 您的查询。<apex:inputField>isPersonAccount
- 如果创建新帐户并设置名称,则记录将为 企业帐户。如果您创建一个新帐户并设置姓氏, 这将是一个个人帐户。
- 最佳做法是创建一个自定义名称公式字段,该字段将正确呈现 个人帐户和企业帐户,然后使用该字段而不是 Visualforce 页面中的标准字段。
- 如果您计划将 Visualforce 页面包含在 Salesforce AppExchange 软件包中, 在控制器或控制器扩展中,不能显式引用字段 仅存在于个人帐户中。
Visualforce 页面中的执行顺序
当用户查看 Visualforce 页面时,控制器、扩展和组件的实例会关联 页面由服务器创建。这些顺序 元素的执行会影响页面向 用户。要完全了解 Visualforce 页面上元素的执行顺序,您必须首先了解页面的生命周期,即页面在 用户会话的过程。页面的生命周期不是确定的 仅通过页面的内容,还取决于页面的请求方式。 有两种类型的 Visualforce 页面请求:
有关这两种请求的具体细节,请举例说明 页面的生命周期,以及有关如何处理执行顺序的提示 在编写自己的自定义控制器和控制器扩展时, 看:
- Visualforce 页面获取请求的执行顺序
- Visualforce 页面回发请求的执行顺序
- Visualforce 页面执行顺序示例
注意
Visualforce 页面请求的最大响应大小必须低于15兆字节.
Visualforce 页面获取请求的执行顺序
get 请求是初始请求 当用户输入 URL 或 单击链接或按钮,将用户带到新页面。下图显示了 Visualforce 页面如何与控制器扩展或自定义控制器进行交互 GET 请求期间的类:

在上图中,用户最初请求页面,或者 通过输入 URL 或单击链接或按钮。此初始页面 request 称为 get 请求。
- 关联的自定义控制器上的构造函数方法或 调用控制器扩展类,实例化控制器 对象。
- 如果页面包含任何自定义组件,则会创建这些组件并 任何关联的自定义控制器或控制器上的构造函数方法 扩展被执行。如果在自定义组件上设置了属性 使用表达式,表达式在构造函数之后计算 被评估。
- 然后,页面对页面上的任何自定义组件执行任何属性。执行方法后,表达式 ,则计算组件上的属性,以及所有其他方法调用,例如获取 或设置属性值。assignToassignToaction<apex:page>
- 如果页面包含组件,则维护状态所需的所有信息 的数据库在页面请求之间被保存为加密的视图状态。每当页面出现以下情况时,视图状态都会更新: 更新。<apex:form>
- 生成的 HTML 将发送到浏览器。如果有任何客户端 页面上的技术,例如 JavaScript,浏览器执行 他们。
当用户与页面交互时,页面会联系控制器 对象,以执行 action、getter 和 setter 方法。一旦用户发出新的 get 请求,视图状态和 控制器对象将被删除。
注意
如果用户被重定向到 使用相同控制器和相同或适当子集的页面 的控制器扩展,则会发出回发请求。回发时 发出请求,视图状态保持不变。
如果用户交互需要页面更新,例如当 用户单击触发保存的“保存”按钮 操作时,将发出回发请求。更多信息 有关回发请求,请参见 Visualforce 页面回发请求的执行顺序。
有关 get 请求的特定示例,请参见 Visualforce 页面执行顺序示例。
Visualforce 页面回发请求的执行顺序
回发请求是 当用户交互需要页面更新时进行,例如当 用户单击“保存”按钮并触发 保存操作。下图显示了 Visualforce 页面如何与控制器扩展或自定义控制器进行交互 回发请求期间的类:

- 在回发请求期间,将对视图状态进行解码并使用 作为更新页面上值的基础。注意组件 替换为属性 设置为绕过此 阶段。换言之,操作已执行,但未执行 对输入执行验证,页面上不会更改任何数据。immediatetrue
- 对视图状态进行解码后,将计算表达式并 在控制器和任何控制器扩展上设置方法,包括 在为自定义组件定义的控制器中执行 set 方法。除非所有方法都 已成功执行。例如,如果其中一个方法更新 属性,并且由于验证规则或 数据类型不正确,数据不会更新,页面会重新显示 以及相应的错误消息。
- 将执行触发回发请求的操作。如果 该操作成功完成,数据将更新。如果回发 request 将用户返回到同一页面,视图状态将更新。注意上的属性 组件 在回发请求期间不计算。它仅在 GET 请求。action<apex:page>
- 生成的 HTML 将发送到浏览器。
如果回发请求指示页面重定向和重定向 是使用相同控制器和 原始页面的控制器扩展,回发请求 为该页面执行。否则,将执行 get 请求 页面。如果回发请求包含组件,则只有 返回回发请求的 ID 查询参数。<apex:form>
提示
可以使用 上的属性来控制是否执行回发或获取请求。如果设置为 true,则 get 请求被执行。将其设置为 false 不会忽略限制 当且仅当目标 使用相同的控制器和适当的扩展子集。如果设置为 false,则 Target 不满足这些要求,将发出 GET 请求。setRedirectpageReferencesetRedirectsetRedirect
一旦用户被重定向到另一个页面,视图状态和 控制器对象将被删除。
有关回发请求的特定示例,请参见 Visualforce 页面执行顺序示例。
Visualforce 页面执行示例 次序
以下示例说明了 Visualforce 页面作为用户的生命周期 与它交互。示例中使用的页面旨在显示有关以下内容的信息 帐户,页面上变量的值,并允许用户编辑详细信息 如果键值设置为除 false 之外的任何值,则帐户。
设置 Visualforce 页面 示例:
- 为名为componentController
public class componentController { public String selectedValue { get; set { editMode = (value != null); // Side effect here - don't do this! selectedValue = value; } } public Boolean editMode {get; private set;} }
- 创建一个名为 :editMode
<apex:component controller="componentController"> <apex:attribute name="value" type="String" description="Sample component." assignTo="{!selectedValue}"/> <p> Value = {!value}<br/> selectedValue = {!selectedValue}<br/> EditMode = {!EditMode} </p> </apex:component>
- 创建一个名为 :myController
public with sharing class myController { private final Account account; public myController() { account = [select id, name, site, NumberOfEmployees, Industry from Account where id = :ApexPages.currentPage().getParameters().get('id')]; } public Account getAccount() { return account; } public PageReference save() { update account; return null; } public PageReference cancel() { return null; } }
- 创建一个名为lifecycle
public with sharing class lifecycle { private final Account acct; Integer EmpAdd; public lifecycle(myController controller) { this.acct = (Account)controller.getAccount(); } public String getGreeting() { return acct.name + ' Current Information'; } public void resetEmp() { acct.numberofemployees = 10; update acct; } }
- 创建一个名为 :setEmps
<apex:page controller="myController" tabStyle="Account" extensions="lifecycle" action="{!resetEmp}"> <apex:messages /> <apex:pageBlock title="{!greeting}"> <apex:outputLabel value="{!$ObjectType.account.fields.Name.label}: " for="acctName"/> <apex:outputField value="{!account.name}" id="acctName"/> <br/> <apex:outputLabel value="{!$ObjectType.account.fields.NumberOfEmployees.label}: " for="emps"/> <apex:outputField value="{!account.NumberOfEmployees}" id="emps"/> <br/> </apex:pageBlock> <apex:pageBlock title="Variable values"> <c:editMode value="{!$CurrentPage.parameters.key}"/> </apex:pageBlock> <apex:form rendered="{!$CurrentPage.parameters.key = 'true'}"> <apex:pageBlock title="Update the Account" id="thePageBlock"> <apex:pageBlockSection columns="1"> <apex:inputField id="aName" value="{!account.name}"/> <apex:inputField value="{!account.NumberOfEmployees}"/> <apex:pageBlockSectionItem> <apex:outputLabel value="{!$ObjectType.account.fields.Industry.label}" for="acctIndustry"/> <apex:actionRegion> <apex:inputField value="{!account.Industry}" id="acctIndustry"> <apex:actionSupport event="onchange" rerender="thePageBlock" status="status"/> </apex:inputField> </apex:actionRegion> </apex:pageBlockSectionItem> </apex:pageBlockSection> <apex:pageBlockButtons location="bottom"> <apex:commandButton action="{!save}" value="Save"/> <apex:commandButton action="{!cancel}" value="Cancel" immediate="true"/> </apex:pageBlockButtons> </apex:pageBlock> </apex:form> </apex:page>
获取请求示例一
对于第一个示例,请访问页面 使用格式为 https:// Salesforce_instance/apex/setEmps?id=recordId 的 URL, 其中 是实例的名称(例如,),并且是 组织中的客户记录(例如,)。您将看到一个页面,其内容类似于 以后:setEmpsSalesforce_instancena1recordID001D000000IRt53
让我们跟踪生命周期,看看为什么页面会显示它的作用。既然你已经 通过输入 URL 直接请求页面,此页面是 get 的结果 请求,而不是回发请求。
- 在 get 请求中发生的第一件事是 调用自定义控制器和控制器扩展。该方法是 控制器,方法是 扩展上的构造函数。这些对象被执行,并且这两个对象现在存在。 控制器现在有一个名为 的变量,它是使用 URL 中的参数的查询的结果,用于标识 要查询的帐户对象。该扩展现在有一个变量,称为 ,该变量是通过调用 控制器。该方法具有 无副作用。myControllerlifecycleaccountidacctgetAccountgetAccount
- get 请求的下一步是创建自定义组件并执行 关联控制器或控制器扩展上的构造函数方法。页面 包括一个自定义 元件:
<c:editMode value="{!$CurrentPage.parameters.key}"/>
这 自定义组件具有关联的控制器,但控制器没有 Explicit 构造函数。与所有没有显式的 Apex 对象一样 构造函数,对象是使用隐式的、无参数的、公共的 构造 函数。作为创建自定义组件的一部分,value 属性 在自定义组件上设置。在这种情况下,它等于 表达式 。由于我们没有在 URL 中指定属性,因此设置为 null。{!$CurrentPage.parameters.key}keyvalue - 创建自定义组件后,将执行这些自定义组件上的所有属性。属性是 setter 将此属性的值赋给 关联的自定义组件控制器。自定义组件确实有一个方法,因此会执行它。该方法将 on 属性设置为属性。该属性设置为 null,因此设置为 null。assignToassignToeditModeassignToassignToselectedValuevaluevalueselectedValue
- get 请求的下一步是计算组件、表达式和 所需的 getter 和 setter 方法。虽然我们将在下面逐步介绍这些, 请记住,这些评估的顺序是不确定的,可能是 与以下内容不同:action<apex:page>
- 组件 具有一个属性,该属性 调用 上的方法 扩展。该方法将对象上的字段设置为 10。<apex:page>actionresetEmpnumberofemployeesacct
- 页面上有几个计算表达式。让我们关注三个:
- <apex:pageBlock title=”{!greeting}”>该属性调用 getter 方法 生命周期扩展。这在页面上呈现为 “全球媒体时事信息”。title<apex:pageblock>getGreeting
- <apex:form rendered=”{!$CurrentPage.parameters.key = ‘true’}”>设置了 on 属性 基于参数的值。我们在调用 页面,因此不会呈现表单。rendered<apex:form>keykey
- Value = {!value}<br/> selectedValue = {!selectedValue}<br/> EditMode = {!EditMode}此表达式出现在自定义中 元件。我们已经讨论过了,并且已经准备好了 但是,对于 null,则 的值尚未 已知。 是 上的布尔变量。它是根据 是否是 等于 null:valueselectedValueEditModeEditModecomponentControllervalue
set { selectedValue = value; // Side effect here - don't do this! editMode = (value != null); }
由于为 null,因此设置为 自。注意 但是,setter 方法存在副作用 为。如 部分设置,我们还设置为 。由于为 null, 这不会改变任何事情,但这种行为有一个 影响在后面的示例中。valueEditModefalseEditModeeditModeselectedValuevaluevalue
- 其他表达式和方法的计算方式类似 方式。
- 由于组件不是 呈现时,不会创建视图状态。<apex:form>
- get 请求的最后一步是将 HTML 发送到浏览器,浏览器 呈现 HTML。
获取请求示例二
对于第二个示例,使用格式为 https:// Salesforce_instance/apex/setEmps?id=recordId&key=false 的 URL 访问页面。 其中 是实例的名称(例如,),并且是 ID 组织中的客户记录(例如,)。与第一个示例不同,此示例包括 第二个参数 key=false。您将看到一个包含内容的页面 类似于以下内容:setEmpsSalesforce_instancena1recordID001D000000IRt53
让我们再次跟踪生命周期。此页面也是 get 请求的结果:
- 在 get 请求中发生的第一件事是 调用自定义控制器和控制器扩展。该方法是 上的构造函数 控制器和方法 是扩展的构造函数。这些被执行,现在有两个对象 存在。控制器现在有一个变量,称为 ,它是查询的结果,该查询使用 URL 中的参数来标识哪个 要查询的客户记录。该扩展现在有一个变量,称为 ,该变量是通过调用 控制器。myControllerlifecycleaccountidacctgetAccount
- get 请求的下一步是创建自定义组件并执行 关联控制器或控制器扩展上的构造函数方法。页面 包括一个自定义 元件:
<c:editMode value="{!$CurrentPage.parameters.key}"/>
这 自定义组件具有没有构造函数的关联控制器,因此 控制器对象是使用隐式的、无参数的、公共的 构造 函数。作为创建自定义组件的一部分,自定义组件上的属性 已设置。在这种情况下,它等于表达式 的结果。我们 将属性指定为 false,因此设置为 假。value{!$CurrentPage.parameters.key}keyvalue - 创建自定义组件后,将执行这些自定义组件上的所有属性。该方法将 on 属性设置为属性。该属性设置为 false,因此设置为 false。assignToassignToselectedValuevaluevalueselectedValue
- get 请求的下一步是计算组件、表达式和 所需的 getter 和 setter 方法。虽然我们将在下面逐步介绍这些, 请记住,这些评估的顺序是不确定的,可能是 与以下内容不同:action<apex:page>
- 组件 具有一个属性,该属性 调用 上的方法 扩展。该方法将对象上的字段设置为 10。<apex:page>actionresetEmpnumberofemployeesacct
- 在页面上的表达式中,让我们看看我们选择的三个是如何计算的:<apex:pageBlock title=”{!greeting}”>属性 on 调用 getter 方法 on 生命周期扩展。它在页面上呈现为 “全球媒体时事信息”。title<apex:pageblock>getGreeting<apex:form rendered=”{!$CurrentPage.parameters.key = ‘true’}”>呈现的属性 on 是根据 参数。 我们设置为何时 调用页面,因此不会呈现表单。<apex:form>keykeyfalseValue = {!value}<br/> selectedValue = {!selectedValue}<br/> EditMode = {!EditMode}此表达式出现在自定义组件中。既然不是 null,是 设置为 。在 此点设置为 null。记得 但是,setter 方法具有 副作用。在本例中,副作用设置为属性 在自定义组件上。由于设置为 ,设置为 假。这说明了为什么你不应该使用副作用 在你的方法中。如果评估顺序不同,并且 的值是在 setter for 的计算结果仍为 null。执行 订单不保证,结果可能 更改下次访问此页面的时间。valueEditModetrueselectedValueEditModeselectedValuevaluevaluefalseselectedValueselectedValueEditModeselectedValueselectedValue警告不要在吸气剂中使用副作用 或二传手!
- 由于组件不是 呈现时,不会创建视图状态<apex:form>
- get 请求的最后一步是将 HTML 发送到浏览器,浏览器 呈现 HTML。
获取请求示例三
对于第三个示例,使用格式为 https:// Salesforce_instance/apex/setEmps?id=recordId&key=true 的 URL 访问页面。 其中 是实例的名称(例如,),并且是 组织中的客户记录(例如,)。与第二个示例不同,此示例设置 key=true。您将看到一个页面,其内容类似于 以后:setEmpsSalesforce_instancena1recordID001D000000IRt53
让我们再跟踪一次 get 请求生命周期:
- 在 get 请求中发生的第一件事是 调用自定义控制器和控制器扩展。该方法是 控制器,方法是 扩展上的构造函数。这些对象被执行,并且这两个对象现在存在。 控制器现在有一个变量,称为 ,它是查询的结果,该查询使用 URL 中的参数来标识哪个 要查询的客户记录。该扩展现在有一个变量,称为 ,该变量是通过调用 控制器。myControllerlifecycleaccountidacctgetAccount
- get 请求的下一步是创建自定义组件并执行 关联控制器或控制器扩展上的构造函数方法。页面 包括一个自定义 元件:
<c:editMode value="{!$CurrentPage.parameters.key}"/>
这 自定义组件具有没有构造函数的关联控制器,因此 控制器对象是使用隐式的、无参数的、公共的 构造 函数。作为创建自定义组件的一部分,自定义组件上的属性 已设置。在这种情况下,它等于表达式 的结果。我们 将属性指定为 true,则设置为 真。value{!$CurrentPage.parameters.key}keyvalue - 创建自定义组件后,将执行这些自定义组件上的所有属性。该方法将 on 属性设置为属性。该属性设置为 true,因此设置为 true。assignToassignToselectedValuevaluevalueselectedValue
- get 请求的下一步是计算组件、表达式和 所需的 getter 和 setter 方法。虽然我们将在下面逐步介绍这些, 请记住,这些评估的顺序是不确定的,可能是 与以下内容不同:action<apex:page>
- 组件 具有一个属性,该属性 调用 上的方法 扩展。该方法将对象上的字段设置为 10。<apex:page>actionresetEmpnumberofemployeesacct
- 在页面上的表达式中,让我们看看我们选择的三个是如何计算的:<apex:pageBlock title=”{!greeting}”>属性 on 调用 getter 方法 on 生命周期扩展。它在页面上呈现为 “全球媒体时事信息”。title<apex:pageblock>getGreeting<apex:form rendered=”{!$CurrentPage.parameters.key = ‘true’}”>属性 on 是根据 参数。 我们设置为何时 调用页面,以便呈现表单。rendered<apex:form>keykeytrueValue = {!value}<br/> selectedValue = {!selectedValue}<br/> EditMode = {!EditMode}此表达式出现在自定义组件中。既然不是 null,是 设置为 。如 前面的示例设置为 null。副作用 在 setter 方法中设置为 。valueEditModetrueselectedValueEditModeselectedValuetrue
- 由于组件是 呈现时,将创建视图状态。<apex:form>
- get 请求的最后一步是将 HTML 发送到浏览器,浏览器 呈现 HTML。
回发请求示例
与前两个示例不同,第三个示例呈现了可编辑的最终页面 字段可单击按钮。要了解回发请求的工作原理,请使用最终的 页面,将帐户名称更改为“Pan Galactic Media”, 员工人数为 42 人“,行业人数为 ”其他”。然后 点击保存。这将启动回发请求:
- 回发请求中发生的第一件事是视图状态为 解码。视图状态包含呈现 页。如果在回发请求期间操作失败,则视图状态为 用于向用户显示页面。
- 接下来,对控制器和控制器上的所有表达式和方法进行计算 扩展被执行。在页面上的表达式中,让我们看看我们的 对选定的三个进行评估:<apex:pageBlock title=”{!greeting}”>属性 on 调用生命周期扩展上的 getter 方法。在我们的编辑中, 我们更改了帐户名称的值。因此,值更改为 “泛银河媒体当前信息。”title<apex:pageblock>getGreetinggreeting<apex:form rendered=”{!$CurrentPage.parameters.key = ‘true’}”>属性 on 是 根据参数的值进行设置。我们没有更改参数,因此值 在视图状态中。由于该值为 true,因此当 视图状态已创建,它仍然是 true,表单是 呈现。rendered<apex:form>keykeyValue = {!value}<br/> selectedValue = {!selectedValue}<br/> EditMode = {!EditMode}我们没有更改这些值中的任何一个,因此,对于每个 表达式,则使用视图状态中的值。
- 最后,保存操作(触发回发请求的操作)是 评价。保存操作是以下方法 控制器:
public PageReference save() { update account; return null; }
这 方法使用新数据更新记录。如果此方法失败,则 如果用户没有更新记录的权限,或者如果 有由更改触发的验证规则,页面是 与描述错误的错误消息一起显示。值 输入的用户不会丢失。它们保持用户单击时的状态 “保存”按钮。假设没有错误,则 对象上的数据会更新,视图状态会更新,并且,由于 触发回发的操作不包括页面重定向,即视图 状态已更新。生成的 HTML 将发送到浏览器:
测试自定义控制器和控制器扩展
控制器扩展和自定义控制器,与所有 Apex 脚本一样,应由 单元测试。单元测试是验证特定代码段是否为类的方法 工作正常。单元测试方法不带参数,不向 数据库,并在方法定义中使用关键字进行标记。testMethod
在为控制器扩展和自定义控制器类编写单元测试时,可以 设置可在测试中使用的查询参数。例如,以下 自定义控制器和标记基于控制器方法中的示例,但已扩展为在 URL 中显示以下查询参数 对于页面:.测试方法类 下面,它练习了此页面的功能:?qp=yyyy
public class thecontroller {
private String firstName;
private String lastName;
private String company;
private String email;
private String qp;
public thecontroller() {
this.qp = ApexPages.currentPage().getParameters().get('qp');
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getCompany() {
return this.company;
}
public void setCompany(String company) {
this.company = company;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public PageReference save() {
PageReference p = null;
if (this.qp == null || !'yyyy'.equals(this.qp)) {
p = Page.failure;
p.getParameters().put('error', 'noParam');
} else {
try {
Lead newlead = new Lead(LastName=this.lastName,
FirstName=this.firstName,
Company=this.company,
Email=this.email);
insert newlead;
} catch (Exception e) {
p = Page.failure;
p.getParameters().put('error', 'noInsert');
}
}
if (p == null) {
p = Page.success;
}
p.setRedirect(true);
return p;
}
}
控制器调用另外两个页面:成功页面和失败页面。文本 对于此示例,这些页面并不重要。它们只需要存在。
以下标记使用上面的控制器:
<apex:page controller="thecontroller" tabstyle="lead">
<apex:pageBlock>
<apex:form>
<h1>Test page for adding leads</h1>
<p>This is a test page for adding leads.</p>
<p>First name: <apex:inputText value="{!FirstName}"></apex:inputText></p>
<p>Last name: <apex:inputText value="{!LastName}"></apex:inputText></p>
<p>Company: <apex:inputText value="{!Company}"></apex:inputText></p>
<p>Email address: <apex:inputText value="{!Email}"></apex:inputText></p>
<apex:commandButton action="{!save}" value="Save New Lead"/>
</apex:form>
</apex:pageBlock>
</apex:page>
以下类测试控制器:
@isTest
public class thecontrollerTests {
public static testMethod void testMyController() {
PageReference pageRef = Page.success;
Test.setCurrentPage(pageRef);
thecontroller controller = new thecontroller();
String nextPage = controller.save().getUrl();
// Verify that page fails without parameters
System.assertEquals('/apex/failure?error=noParam', nextPage);
// Add parameters to page URL
ApexPages.currentPage().getParameters().put('qp', 'yyyy');
// Instantiate a new controller with all parameters in the page
controller = new thecontroller();
controller.setLastName('lastname');
controller.setFirstName('firstname');
controller.setCompany('acme');
controller.setEmail('firstlast@acme.com');
nextPage = controller.save().getUrl();
// Verify that the success page displays
System.assertEquals('/apex/success', nextPage);
Lead[] leads = [select id, email from lead where Company = 'acme'];
System.assertEquals('firstlast@acme.com', leads[0].email);
}
}
提示
如果您正在测试控制器,您可能会看到以下错误消息:
Method does not exist or incorrect signature: Test.setCurrentPage(System.PageReference)
验证规则和自定义控制器
如果用户在使用自定义控制器的 Visualforce 页面上输入数据,并且该数据导致验证 规则错误,该错误可以显示在 Visualforce 页面上。就像使用标准控制器的页面一样,如果验证 规则错误位置是与组件关联的字段, 错误显示在那里。如果设置了验证规则错误位置 在页面顶部,使用 中的组件 显示错误。但是,要获取页面信息, 自定义控制器必须捕获异常。<apex:inputField><apex:messages><apex:page>例如,假设您有以下内容 页:
<apex:page controller="MyController" tabStyle="Account">
<apex:messages/>
<apex:form>
<apex:pageBlock title="Hello {!$User.FirstName}!">
This is your new page for the {!name} controller. <br/>
You are viewing the {!account.name} account.<br/><br/>
Change Account Name: <p></p>
<apex:inputField value="{!account.name}"/> <p></p>
Change Number of Locations:
<apex:inputField value="{!account.NumberofLocations__c}" id="Custom_validation"/>
<p>(Try entering a non-numeric character here, then hit save.)</p><br/><br/>
<apex:commandButton action="{!save}" value="Save New Account Name"/>
</apex:pageBlock>
</apex:form>
</apex:page>
注意
这 必须在此页的 URL 中将有效客户记录的 ID 指定为查询参数 进行渲染。例如,http:// MyDomainName.salesforce.com/apex/myValidationPage?id=001x000xxx3Jsxb。你 需要编写一个自定义控制器,例如 以后:
public class MyController {
Account account;
public PageReference save() {
try{
update account;
}
catch(DmlException ex){
ApexPages.addMessages(ex);
}
return null;
}
public String getName() {
return 'MyController';
}
public Account getAccount() {
if(account == null)
account = [select id, name, numberoflocations__c from Account
where id = :ApexPages.currentPage().getParameters().get('id')];
return account;
}
}
什么时候 用户保存页面,如果触发验证错误,则捕获异常并 在页面上显示,就像标准控制器一样。
使用 transient 关键字
使用关键字声明实例 无法保存且不应作为视图状态的一部分传输的变量 用于 Visualforce 页面。为 例:
transient
Transient Integer currentTotal;
您还可以在 Apex 中使用关键字 可序列化的类,即在控制器、控制器扩展或类中 实现 or 接口。此外,还可以在定义类型的类中使用 在可序列化类中声明的字段。transientBatchableSchedulabletransient
将变量声明为缩减视图 状态大小。关键字的一个常见用例是 Visualforce 页面上的字段,该字段仅在页面持续时间内需要 请求,但不应是页面视图状态的一部分,并且会使用过多的系统 在请求期间要多次重新计算的资源。transienttransient
某些 Apex 对象会自动被视为瞬态对象,也就是说,它们的值不会 保存为页面视图状态的一部分。这些对象包括:
- 页面引用
- XmlStream 类
- 集合自动标记为暂时性,仅当它们的对象类型 保留会自动标记为暂时性保留,例如保存点的集合
- 大多数对象由系统方法生成,例如 .Schema.getGlobalDescribe
- JSONParser类实例。
静态变量也不会得到 通过视图状态传输。
以下示例包含一个 Visualforce 页面和一个自定义控制器。点击 页面上的“刷新”按钮使瞬态日期为 已更新,因为每次刷新页面时都会重新创建它。非瞬态 date 继续具有其原始值,该值已从视图中反序列化 状态,所以它保持不变。
<apex:page controller="ExampleController">
T1: {!t1} <br/>
T2: {!t2} <br/>
<apex:form>
<apex:commandLink value="refresh"/>
</apex:form>
</apex:page>
public class ExampleController {
DateTime t1;
transient DateTime t2;
public String getT1() {
if (t1 == null) t1 = System.now();
return '' + t1;
}
public String getT2() {
if (t2 == null) t2 = System.now();
return '' + t2;
}
}