Visualforce 的主要目的是 一种静态的标记驱动语言,允许开发人员创建与 Salesforce 外观。但是,有 是需要以编程方式创建页面的情况。通常,这是为了 实现标准难以或不可能实现的复杂用户界面行为 标记。
动态视觉力 组件提供了一种创建 Visualforce 页面的方法,该页面根据各种 状态,例如用户的权限或操作、用户或组织首选项、 正在显示的数据,等等。动态 Visualforce 组件不是使用标准标记,而是在 顶点。动态 Visualforce 组件在 Apex 中定义,如下所示 这:
Component.Component_namespace.Component_name
为 示例,则变为 .
<apex:dataTable>Component.Apex.DataTable
注意
标准元件参考包含 所有有效 Visualforce 组件的动态表示。在 Apex 中动态表示的 Visualforce 组件的行为类似于常规类。每 存在于标准 Visualforce 组件上的属性可作为 属性,以及 get 和 set 方法。例如,您 可以将组件上的属性操作为 遵循:
value<apex:outputText>
Component.Apex.OutputText outText = new Component.Apex.OutputText();
outText.value = 'Some dynamic output text.';
请考虑在以下情况下使用动态 Visualforce 组件:
- 您可以使用动态 Visualforce 复杂控制逻辑中的组件,以组合方式组装组件 使用同等标准的 Visualforce 进行创作具有挑战性或不可能。例如,使用 标准 Visualforce 组件,您 通常使用具有全局公式函数的属性来控制组件的可见性。通过在 Apex 中编写控制逻辑,您可以选择显示组件 动态地具有更自然的机制。renderedIF()
- 如果您知道将遍历具有某些字段的对象,但不是 具体到哪些对象,动态 Visualforce 组件可以“插入 in“,使用泛型 sObject 引用表示对象。查看更多 信息,请参阅使用相关列表的示例。
警告
动态视觉力 组件不是在组织中创建新 Visualforce 页面的主要方式。 现有 Visualforce 页面 不应以动态方式重写,对于大多数用例,标准的 Visualforce 组件是可以接受的,并且 首选。仅当页面必须时,才应使用动态 Visualforce 组件 以无法优雅地编码为静态的方式适应用户状态或操作 标记。
动态组件限制
并非 Visualforce 的每个功能都能使 在动态上下文中感知,因此某些组件不能动态使用。
- 以下标准 Visualforce 组件在 Apex 中没有相应的动态表示:
- <apex:attribute>
- <apex:component>
- <apex:componentBody>
- <apex:composition>
- <apex:define>
- <apex:dynamicComponent>
- <apex:include>
- <apex:insert>
- <apex:param>
- <apex:variable>
- 如果动态 Visualforce 组件 引用特定的 sObject 字段,该字段稍后被删除,即该字段的 Apex 代码 字段引用仍将编译,但页面在查看时将失败。此外,您还可以 创建对全局变量(如 或)的引用,然后删除引用的项,使用 类似的结果。请验证此类页面是否继续按预期工作。$Setup$Label
- 动态 Visualforce 页面和 表达式比静态页面更严格地检查属性类型。
- 您无法在动态上设置“直通”HTML 属性 组件。
创建和显示动态组件
注意
出于教学目的,本节中的示例特意简单。对于一个 有关何时可能从动态 Visualforce 组件中受益的更完整示例,请参阅使用相关列表的示例。在页面上嵌入动态 Visualforce 组件分为两部分:
- 添加标签 页面上的某个位置。此标记充当动态的占位符 元件。<apex:dynamicComponent>
- 在 您的控制器或控制器扩展。
标签有一个 required 属性——接受返回 动态组件。例如,如果要动态生成 部分标题不同 如果提交表单的截止日期已过,您可以 使用以下标记和控制器 法典:
<apex:dynamicComponent>componentValue
<apex:page standardController="Contact" extensions="DynamicComponentExample">
<apex:dynamicComponent componentValue="{!headerWithDueDateCheck}"/>
<apex:form>
<apex:inputField value="{!Contact.LastName}"/>
<apex:commandButton value="Save" action="{!save}"/>
</apex:form>
</apex:page>
public class DynamicComponentExample {
public DynamicComponentExample(ApexPages.StandardController con) { }
public Component.Apex.SectionHeader getHeaderWithDueDateCheck() {
date dueDate = date.newInstance(2011, 7, 4);
boolean overdue = date.today().daysBetween(dueDate) < 0;
Component.Apex.SectionHeader sectionHeader = new Component.Apex.SectionHeader();
if (overdue) {
sectionHeader.title = 'This Form Was Due On ' + dueDate.format() + '!';
return sectionHeader;
} else {
sectionHeader.title = 'Form Submission';
return sectionHeader;
}
}
}
你 单个页面上可以有多个组件。
<apex:dynamicComponent>
每 动态组件可以访问一组通用的方法和属性。您可以查看 此列表在 Apex 开发人员指南中标题为“组件类”的章节中。
动态自定义组件
动态使用自定义组件的工作方式与标准 Visualforce 组件完全相同。只 将命名空间更改为自定义组件的命名空间。您的自定义组件位于 命名空间,以便您可以创建一个命名空间 动态喜欢 这:
c
Component.c.MyCustomComponent myDy = new Component.c.MyCustomComponent();
为了方便您自己的组件,您可以省略命名空间,例如 所以:
Component.MyCustomComponent myDy = new Component.MyCustomComponent();
如果在包中使用第三方提供的组件,请使用命名空间 包装的 供应商:
Component.TheirName.UsefulComponent usefulC = new Component.TheirName.UsefulComponent();
通过构造函数传递属性
无需通过其属性设置组件属性,只需传入即可 一个或多个属性的列表,通过 构造 函数:
Component.Apex.DataList dynDataList =
new Component.Apex.DataList(id='myDataList', rendered=true);
如果 构造函数中未定义属性,即组件的默认值 用于该属性。有两个组件必须在构造函数中定义一个属性: 而不是通过属性:
- Component.Apex.Detail必须已经传递给它的 构造函数,如果要显示 的 Chatter 信息和控件 一个记录。否则,此属性始终为 false。showChatter=true
- Component.Apex.SelectList必须已经传递给它的 构造函数(如果希望用户能够选择多个选项) 一次。否则,此值始终为 。multiSelect=truefalse
这些值是布尔值,而不是字符串;你不需要把它们封闭起来 用单引号引起来。
警告
不能通过类构造函数传递属性 如果属性名称与 Apex 关键字匹配。例如,不能通过构造函数, 因为 List 是保留关键字。同样,无法在构造函数中定义属性,因为 这也是一个关键词。Component.Apex.RelatedListlistComponent.Apex.OutputLabelfor
定义表达式和任意 HTML
可以使用该属性添加表达式语言语句。追加在属性名称之前以传入表达式语句。 与静态标记一样,表达式必须使用语法进行包装。这是一个 例:
expressionsexpressions{! }
Component.Apex.Detail detail = new Component.Apex.Detail();
detail.expressions.subject = '{!Account.ownerId}';
detail.relatedList = false;
detail.title = false;
有效表达式包括引用标准对象和自定义对象上的字段的表达式。 全局变量和函数也可用,如下所示 例:
Component.Apex.OutputText head1 = new Component.Apex.OutputText();
head1.expressions.value =
'{!IF(CONTAINS($User.FirstName, "John"), "Hello John", "Hey, you!")}';
通过表达式传入值仅对支持值的属性有效。 在酒店外使用将被解释 从字面上看,不是作为表达。{! }expressions如果要包含纯 HTML,可以通过将属性设置为:
escapeComponent.Apex.OutputTextfalse
Component.Apex.OutputText head1 = new Component.Apex.OutputText();
head1.escape = false;
head1.value = '<h1>This header contains HTML</h1>';
定义分面
与定义表达式的方式类似,分面充当特殊属性 可用于动态组件。这是一个 例:
Component.Apex.DataTable myTable = new Component.Apex.DataTable(var='item');
myTable.expressions.value = '{!items}';
Component.Apex.OutputText header =
new Component.Apex.OutputText(value='This is My Header');
myTable.facets.header = header;
有关分面的详细信息,请参阅使用组件分面的最佳实践。
定义子节点
您可以使用以下命令将子节点添加到动态 Visualforce 组件 该物业。该属性充当对 a 对象清单。childComponentschildComponentsComponent.Apex下面是一个示例,说明如何使用子输入来构造 节点:
childComponents<apex:form>
public Component.Apex.PageBlock getDynamicForm() {
Component.Apex.PageBlock dynPageBlock = new Component.Apex.PageBlock();
// Create an input field for Account Name
Component.Apex.InputField theNameField = new Component.Apex.InputField();
theNameField.expressions.value = '{!Account.Name}';
theNameField.id = 'theName';
Component.Apex.OutputLabel theNameLabel = new Component.Apex.OutputLabel();
theNameLabel.value = 'Rename Account?';
theNameLabel.for = 'theName';
// Create an input field for Account Number
Component.Apex.InputField theAccountNumberField = new Component.Apex.InputField();
theAccountNumberField.expressions.value = '{!Account.AccountNumber}';
theAccountNumberField.id = 'theAccountNumber';
Component.Apex.OutputLabel theAccountNumberLabel = new Component.Apex.OutputLabel();
theAccountNumberLabel.value = 'Change Account #?';
theAccountNumberLabel.for = 'theAccountNumber';
// Create a button to submit the form
Component.Apex.CommandButton saveButton = new Component.Apex.CommandButton();
saveButton.value = 'Save';
saveButton.expressions.action = '{!Save}';
// Assemble the form components
dynPageBlock.childComponents.add(theNameLabel);
dynPageBlock.childComponents.add(theNameField);
dynPageBlock.childComponents.add(theAccountNumberLabel);
dynPageBlock.childComponents.add(theAccountNumberField);
dynPageBlock.childComponents.add(saveButton);
return dynPageBlock;
}
如果标记定义为:
<apex:form>
<apex:dynamicComponent componentValue="{!dynamicForm}"/>
</apex:form>
然后 您的标记等效于以下静态 标记:
<apex:form>
<apex:pageBlock>
<apex:outputLabel for="theName"/>
<apex:inputField value="{!Account.Name}" id="theName"/>
<apex:outputLabel for="theAccountNumber"/>
<apex:inputField value="{!Account.AccountNumber}" id="theAccountNumber"/>
<apex:commandButton value="Save" action="{!save}"/>
</apex:pageBlock>
</apex:form>
通知 等效静态标记中元素的顺序是 动态组件被添加到 ,而不是它们在 Apex 中声明的顺序 方法的代码。
childComponentsgetDynamicForm
延迟创建动态组件
默认情况下,定义动态组件的 Apex 方法在页面加载时执行 时间,在运行为页面定义的任何操作方法之前。将动态组件的属性设置为等待页面操作完成 在创建动态组件的方法运行之前。这使您能够设计 动态组件,根据页面的结果而变化,例如,页面 初始化操作或标注。
invokeAfterActiontrue下面是一个具有单个动态组件的页面,该组件是在 页面的 action 方法 , 是 完成。
pageActionUpdateMessage
<apex:page controller="DeferredDynamicComponentController"
action="{!pageActionUpdateMessage}" showHeader="false">
<apex:dynamicComponent componentValue="{!dynamicComp}" invokeAfterAction="true"/>
</apex:page>
下面是提供动态组件定义的关联控制器: 并说明了属性的效果。
invokeAfterAction
public class DeferredDynamicComponentController {
private String msgText { get; set; }
public DeferredDynamicComponentController() {
this.msgText = 'The controller is constructed.';
}
public Component.Apex.OutputPanel getDynamicComp() {
// This is the component to return
Component.Apex.OutputPanel dynOutPanel= new Component.Apex.OutputPanel();
dynOutPanel.layout = 'block';
// Child component to hold the message text
Component.Apex.OutputText msgOutput = new Component.Apex.OutputText();
msgOutput.value = this.msgText;
dynOutPanel.childComponents.add(msgOutput);
return dynOutPanel;
}
public Object pageActionUpdateMessage() {
this.msgText= 'The page action method has been run.';
return null;
}
}
跟 动态组件的默认行为,在构造函数中设置的值由 动态组件。在动态组件上进行设置会更改该行为。 页面等待 完成,然后创建动态组件,因此组件 显示已设置的值 在 Action 方法中 相反。
msgTextinvokeAfterAction=”true”pageActionUpdateMethodmsgTextpageActionUpdateMessage
注意
该属性可用 对于设置为 API 版本 31.0 或更高版本的页面中的动态组件。invokeAfterAction
延迟创建动态组件和其他操作
invokeAfterAction=”true”影响动态 组件在页面加载时立即生效,因为那是页面操作的时候 跑。设置将颠倒组件创建顺序和页面上的任何操作方法。 也就是说,以下所有组件上的方法的执行顺序都已更改。
invokeAfterAction=”true”action
- <apex:actionFunction>
- <apex:actionPoller>
- <apex:actionSupport>
- <apex:commandButton>
- <apex:commandLink>
- <apex:page>
When 设置为 动态组件,执行顺序如下。这是默认设置 动态组件的行为。
invokeAfterAction=”false”
- 调用动态组件的创建方法,该方法构造 元件。
- 调用 action 方法。
- 重新呈现页面。
When 设置为 动态组件,执行顺序如下。
invokeAfterAction=”true”
- 调用 action 方法。
- 调用动态组件的创建方法,该方法构造 元件。
- 重新呈现页面。
注意
在第二种情况下,如果操作方法返回 PageReference,则 Visualforce 将重定向 对新页面的请求,以及动态组件的创建方法 不会运行。为了避免可能的执行顺序错误,这是一个 创建动态组件的方法没有侧的最佳实践 影响。
使用相关列表的示例
动态 Visualforce 组件包括 当您不知道要引用的对象类型时,最好使用 与动态 Visualforce 绑定相反, 当您不知道要访问的字段时,最好使用。
以下使用动态 Visualforce 的场景构建了一个简单的 具有要访问的一组已知字段的可重用页面。页面及其自定义 对象被放置在一个非托管包中,并分布在同一个包中 组织。首先,创建一个名为 Classroom 的自定义对象。创建两个对象 – 一个命名,另一个命名,如下图所示:
Science 101Math 201

接下来,再创建两个自定义对象,分别称为 Student 和 Teacher。完成后 创建每个对象:
- 单击“自定义字段”下的“新建”& 关系。
- 选择“主从关系”,然后单击“下一步”。
- 从下拉列表中选择“课堂”,然后单击“下一步”。
- 继续单击“下一步”,保留所有默认值 完整。
创建以下对象和匹配关系:
- 一个名为 的新学生和新老师 named ,两者都分配给 。Johnny WalkerMister PibbScience 101
- 另一个名叫 的新学生和一位新老师 named ,两者都分配给 。Boont AmberDoctor PepperMath 201
现在,创建一个新的 Apex 页面,称为并粘贴 以下 法典:
DynamicClassroomList
public class DynamicClassroomList {
private ApexPages.StandardSetController controller;
private PageReference savePage;
private Set<String> unSelectedNames;
private Set<String> selectedNames;
public List<String> selected { get; set; }
public List<String> unselected { get; set; }
public String objId { get; set; }
public List<String> displayObjs {
get; private set;
}
boolean idIsSet = false;
public DynamicClassroomList() {
init();
}
public DynamicClassroomList(ApexPages.StandardSetController con) {
this.controller = con;
init();
}
private void init() {
savePage = null;
unSelectedNames = new Set<String>();
selectedNames = new Set<String>();
if (idIsSet) {
ApexPages.CurrentPage().getParameters().put('id', objId);
idIsSet = false;
}
}
public PageReference show() {
savePage = Page.dynVFClassroom;
savePage.getParameters().put('id', objId);
return savePage;
}
public List<SelectOption> displayObjsList {
get {
List<SelectOption> options = new List<SelectOption>();
List<Classroom__c> classrooms = [SELECT id, name FROM Classroom__c];
for (Classroom__c c: classrooms) {
options.add(new SelectOption(c.id, c.name));
}
return options;
}
}
public PageReference customize() {
savePage = ApexPages.CurrentPage();
savePage.getParameters().put('id', objId);
return Page.dynamicclassroomlist;
}
// The methods below are for constructing the select list
public List<SelectOption> selectedOptions {
get {
List<String> sorted = new List<String>(selectedNames);
sorted.sort();
List<SelectOption> options = new List<SelectOption>();
for (String s: sorted) {
options.add(new SelectOption(s, s));
}
return options;
}
}
public List<SelectOption> unSelectedOptions {
get {
Schema.DescribeSObjectResult R = Classroom__c.SObjectType.getDescribe();
List<Schema.ChildRelationship> C = R.getChildRelationships();
List<SelectOption> options = new List<SelectOption>();
for (Schema.ChildRelationship cr: C) {
String relName = cr.getRelationshipName();
// We're only interested in custom relationships
if (relName != null && relName.contains('__r')) {
options.add(new SelectOption(relName, relName));
}
}
return options;
}
}
public void doSelect() {
for (String s: selected) {
selectedNames.add(s);
unselectedNames.remove(s);
}
}
public void doUnSelect() {
for (String s: unselected) {
unSelectedNames.add(s);
selectedNames.remove(s);
}
}
public Component.Apex.OutputPanel getClassroomRelatedLists() {
Component.Apex.OutputPanel dynOutPanel= new Component.Apex.OutputPanel();
for(String id: selectedNames) {
Component.Apex.RelatedList dynRelList = new Component.Apex.RelatedList();
dynRelList.list = id;
dynOutPanel.childComponents.add(dynRelList);
}
return dynOutPanel;
}
}
后 尝试保存时,系统可能会提示您缺少 Visualforce 页面。点击链接 创建页面:接下来的代码块将填充该页面。创建一个名为 Visualforce 的页面,并粘贴以下内容 法典:
dynVFClassroom
<apex:page standardController="Classroom__c" recordSetVar="classlist"
extensions="DynamicClassroomList">
<apex:dynamicComponent componentValue="{!ClassroomRelatedLists}"/>
<apex:form>
<apex:pageBlock title="Classrooms Available" mode="edit">
<apex:pageMessages/>
<apex:selectRadio value="{!objId}">
<apex:selectOptions value="{!displayObjsList}"/>
</apex:selectRadio>
</apex:pageBlock>
<apex:commandButton value="Select Related Items" action="{!Customize}"/>
</apex:form>
</apex:page>
最后,创建一个名为 .如果 您从一开始就一直在学习本教程,您应该已经 在构造控制器扩展时创建了此页面。粘贴以下内容 法典:
DynamicClassroomList
<apex:page standardController="Classroom__c" recordsetvar="listPageMarker"
extensions="DynamicClassroomList">
<apex:messages/><br/>
<apex:form>
<apex:pageBlock title="Select Relationships to Display" id="selectionBlock">
<apex:panelGrid columns="3">
<apex:selectList id="unselected_list" required="false"
value="{!selected}" multiselect="true" size="20"
style="width:250px">
<apex:selectOptions value="{!unSelectedOptions}"/>
</apex:selectList>
<apex:panelGroup>
<apex:commandButton value=">>" action="{!DoSelect}"
reRender="selectionBlock"/>
<br/>
<apex:commandButton value="<<" action="{!DoUnselect}"
reRender="selectionBlock"/>
</apex:panelGroup>
<apex:selectList id="selected_list" required="false"
value="{!unselected}" multiselect="true" size="20"
style="width:250px">
<apex:selectOptions value="{!selectedOptions}"/>
</apex:selectList>
</apex:panelGrid>
</apex:pageBlock>
<br/>
<apex:commandButton value="Show Related Lists" action="{!show}"/>
</apex:form>
</apex:page>
这 是向用户提供选择哪个对象的选项的页面 要显示的关系。请注意,“selected”和 “未选择”列表通过动态方式填充。
组装控制器扩展和这些页面后,导航到组织中的 /apex/dynVFClassroom。你会看到一个 类似于以下内容的序列: