使用标准列表控制器
学习目标
完成本单元后,您将能够:
- 解释 Visualforce 标准列表控制器的含义,以及与标准(记录)控制器的区别。
- 列出标准列表控制器与标准控制器不同的三个操作。
- 在 Visualforce 页面上使用标准列表控制器显示记录列表。
- 定义分页,并将其添加到 Visualforce 页面。
标准列表控制器简介
标准列表控制器允许您创建可以显示或操作一组记录的 Visualforce 页面。
显示记录列表是几乎所有 Web 应用程序的基本行为。Visualforce 只需使用标记,无需后端代码,就可以非常轻松地显示相同类型的记录列表。以往,标准控制器是秘诀,在这种情况下秘诀是标准列表控制器。
标准列表控制器提供了诸多强大的自动行为,例如查询特定对象的记录并使记录在集合变量中可用,以及对结果进行筛选和分页。将标准列表控制器添加到页面与添加标准(记录)控制器非常相似,目的是一次处理多条记录,而不是只处理一条记录。
显示记录列表
使用标准列表控制器和迭代组件,例如使用 <apex:pageBlockTable>
显示记录列表。
标准(记录)控制器可以轻松地将单个记录加载到可以在 Visualforce 页面上使用的变量中。标准列表控制器与之类似,不同的是,它不是将单个记录加载到变量中,而是将记录的列表或集合加载到变量中。
因为您处理的是一个集合,而不是单个记录,所以需要使用迭代组件来显示。迭代组件处理类似项的集合,而不是单个值。迭代组件循环遍历集合,并且对于每个记录,根据作为组件标记的一部分提供的模板生成输出。这听起来很复杂,但是当您阅读标记时,也很快就能理解。
使用标准列表控制器的标记几乎与使用标准的、一次一条记录的控制器相同。为清楚起见,以下示例中以粗体突出显示主要差异。
- 要创建新的 Visualforce 页面,请打开 Developer Console,然后单击 File(文件) | New(新建) | Visualforce Page(Visualforce 页面)。输入 ContactList 作为页面名称。
- 在编辑器中,将标记替换为以下内容。
<apex:page standardController="Contact" recordSetVar="contacts"> <apex:pageBlock title="Contacts List"> <!-- Contacts List --> <apex:pageBlockTable value="{! contacts }" var="ct"> <apex:column value="{! ct.FirstName }"/> <apex:column value="{! ct.LastName }"/> <apex:column value="{! ct.Email }"/> <apex:column value="{! ct.Account.Name }"/> </apex:pageBlockTable> </apex:pageBlock> </apex:page>
- 单击 Preview(预览)可打开页面预览,您可以在进行更改时查看该预览。此时会打开一个新窗口,显示标准 Salesforce 页面标题、侧栏元素、以及联系人列表。
使用标准列表控制器与使用标准控制器非常相似。首先在 <apex:page>
组件上设置 standardController
属性,然后在同一个组件上设置 recordSetVar
属性。就像使用标准控制器一样,standardController
属性设置要使用的对象。recordSetVar 设置需要使用记录集合创建的变量的名称,此处指 {!contacts }
。按照惯例,变量通常被命名为对象名称的复数形式。
<apex:pageBlockTable>
是一个迭代组件,用于生成数据表,并带有平台样式。以下是表格标记中会发生的事情。
-
<apex:pageBlockTable>
的 value 属性设置为标准列表控制器加载的变量{!contacts }
。这是<apex:pageBlockTable>
将要使用的记录列表。
- 对于列表中的每条记录,一次一条记录,
<apex:pageBlockTable>
将该记录分配给<apex:pageBlockTable>
的 var 属性中命名的变量。本示例中,变量名称为ct
。
- 对于每条记录,
<apex:pageBlockTable>
使用<apex:pageBlockTable>
主体中的<apex:column>
列组件集合定义的行在表中构建一个新行。<Apex:column>
组件继而使用表示当前记录的ct
变量来提取该记录的字段值。
- 在循环的外部,
<apex:pageBlockTable>
使用<apex:column>
组件中的字段,通过查找每个字段标签的方式来创建列标题。
它涉及大量内容,对于首次接触迭代组件的用户而言,这很难理解。现在您最好尝试创建一个自己的组件。选择要在表中显示的字段。查找 <apex:pageBlockTable>
和 <apex:column>
的不同属性,然后进行试验,直到您感觉毫不费力。您还可以尝试使用其他一些迭代组件,例如 <apex:dataList>
、又或者是 <apex:repeat>
。
将筛选列表视图添加到列表中
使用 {! listViewOptions }
获取一个对象可用的列表视图筛选器列表。使用 {! filterId }
设置用于标准列表控制器结果的列表视图筛选器。
标准列表控制器提供了诸多可用于更改列表显示的特性。其中最强大的特性是列表视图筛选器。您可以声明使用单击而不是代码的方式创建列表视图筛选器,标准列表控制器允许您在页面上使用已定义的任意列表视图筛选器。
- 将整个
<apex:pageBlock>
封装在<apex:form>
标签中。要更改标准列表控制器的列表视图筛选器,需要将新值提交回服务器。提交的标准方法是使用通过<apex:form>
组件创建的表单。
- 在
<apex:pageBlock>
标签下,添加以下属性。
id="contacts_list"
这给 <apex:pageBlock>
提供了一个“名称”,我们可以用它来实现很酷的 Ajax 效果,稍后将对此进行解释。
- 在起始
<apex:pageBlock>
标签后面、<apex:pageBlockTable>
的上方,添加以下标记。页面的完整代码如下所示。Filter: <apex:selectList value="{! filterId }" size="1"> <apex:selectOptions value="{! listViewOptions }"/> <apex:actionSupport event="onchange" reRender="contacts_list"/> </apex:selectList>
列表上方出现一个新的筛选器控件。<apex:page standardController="Contact" recordSetVar="contacts"> <apex:form> <apex:pageBlock title="Contacts List" id="contacts_list"> Filter: <apex:selectList value="{! filterId }" size="1"> <apex:selectOptions value="{! listViewOptions }"/> <apex:actionSupport event="onchange" reRender="contacts_list"/> </apex:selectList> <!-- Contacts List --> <apex:pageBlockTable value="{! contacts }" var="ct"> <apex:column value="{! ct.FirstName }"/> <apex:column value="{! ct.LastName }"/> <apex:column value="{! ct.Email }"/> <apex:column value="{! ct.Account.Name }"/> </apex:pageBlockTable> </apex:pageBlock> </apex:form> </apex:page>
- 从菜单中选择不同的筛选器。联系人列表会发生什么?
从刚创建的筛选器菜单中重新选择时,您应该注意到两件事。首先,当您选择新筛选器时,记录列表会发生变化。(您可能需要尝试几个不同的选项,在标准 DE 组织中使用示例数据时,某些列表视图将显示相同的记录。)
其次,更微妙的是,列表会在不重新加载整个页面的情况下更新。这种 “Ajax” 效果是由 <apex:actionSupport>
组件上的 reRender="contacts_list”
属性提供的。组件和 reRender 的组合效果是仅更新在 reRender 属性中命名的页面部分。由于您将 id="contacts_list”
添加到 <apex:pageBlock>
,当操作完成时,在无需重新加载整个页面的情况下,即可实现只更新 <apex:pageBlock>
的效果。
此页面新功能的完整生命周期是这样的。
- 加载页面时,
<apex:selectList>
从{!listViewOptions }
表达式获取列表,创建可用筛选器菜单。listViewOptions
是标准列表控制器提供的属性。
- 当您从菜单中选择一个新选项时,
<apex:actionSupport>
组件会触发onchange
事件。
- 当
onchange
触发时,页面通过将新项目提交给<apex:selectList>
中设置的filterId
属性的方式,提交已选择的新列表视图。
- 当属性更新时,页面从服务器获得新的响应,在 contacts 变量中会有一个新的匹配记录集合。
- 由于
<apex:actionSupport>
指定只重新渲染页面的一部分,页面是使用 Ajax(异步 JavaScript)更新的,而不是通过重新加载整个页面的方式。
最终结果是,只需添加几行标记,即可实现错综复杂的行为。
向列表添加分页
使用标准列表控制器的分页特性,允许用户单次查看一页长的记录列表。
到目前为止,您开发的功能非常好,并且适用于在 Developer Edition 组织中作为样例数据提供简短记录列表。但是,当您拥有一个真正的组织,组织内有着数百或数千甚至数百万条记录时会发生什么?在单页上查看所有记录,效果并不好!
事实上,标准列表控制器默认情况下只显示符合筛选条件的前 20条记录(如果有的话)。如何让用户访问超出前 20 条的记录,或者每页记录超过 20 条?
分页可以解决这个问题。这是一个标准的 Web 应用程序用户界面元素,通常使用下一页和上一页链接,实现在单页的一长串记录中向前或向后移动。您可以使用标准列表控制器将其添加到您的页面中,还可以使用进度指示器和菜单来更改每页记录的数量。
- 在
</apex:pageBlockTable>
联系人列表下方,添加以下标记。
<!-- Pagination --> <table style="width: 100%"> <tr> <td> <!-- Page X of Y --> </td> <td align="center"> <!-- Previous page --> <!-- Next page --> </td> <td align="right"> <!-- Records per page --> </td> </tr> </table>
这将创建一个 HTML 表,其中将包含您要添加的三个分页控件。
- 将
<!-- Page X of Y -->
注释行替换为以下标记。
Page: <apex:outputText value=" {!PageNumber} of {! CEILING(ResultSize / PageSize) }"/>
这会向列表中添加一个进度指示器,指示用户正在查看哪个页面以及剩下多少页面。如果您在 DE 组织中尝试此操作,它可能会显示“Page 1 of 1”(第 1 页,共 1 页)。
- 将
<!-- Previous page -->
和<!-- Next page -->
注释行替换为以下标记。该标记在页面上提供上一页和下一页链接。标记包含两种可能性:当给定方向有更多记录要查看时,链接处于活动状态;当给定方向没有更多页面时,链接处于禁用状态。<!-- Previous page --> <!-- active --> <apex:commandLink action="{! Previous }" value="« Previous" rendered="{! HasPrevious }"/> <!-- inactive (no earlier pages) --> <apex:outputText style="color: #ccc;" value="« Previous" rendered="{! NOT(HasPrevious) }"/> <!-- Next page --> <!-- active --> <apex:commandLink action="{! Next }" value="Next »" rendered="{! HasNext }"/> <!-- inactive (no more pages) --> <apex:outputText style="color: #ccc;" value="Next »" rendered="{! NOT(HasNext) }"/>
- 将
<!-- Records per page -->
注释行替换为以下标记。此标记提供用于更改每页记录数的菜单。在这里,我们只添加了一个选项以在页面上显示少量记录。从列表中选择“5”,看看列表和其他控件会发生什么。Records per page: <apex:selectList value="{! PageSize }" size="1"> <apex:selectOption itemValue="5" itemLabel="5"/> <apex:selectOption itemValue="20" itemLabel="20"/> <apex:actionSupport event="onchange" reRender="contacts_list"/> </apex:selectList>
结果是一个列表页面,其中包含标准列表控制器提供的诸多功能。
在进度指示器中,有三个属性用于指示页数,分别是 PageNumber
、ResultSize
和 PageSize
。最后两个用于公式表达式,将数字四舍五入到最接近的整数。可防止指示器提示无效信息,例如“Page 2 of 1.6”(第 2 页,共 1.6 页)。
在分页控件中,<apex:commmandLink>
组件引用了标准列表控制器提供的两种操作方法,Previous
和 Next
。结果是执行 Previous
或 Next
操作的链接。
不过这种渲染属性是什么?它的表达式为 {!HasPrevious }
。这就是 Visualforce 有条件地显示组件的方式,即取决于布尔表达式的结果。此处的页面标记引用了标准列表控制器 HasPrevious
和 HasNext
提供的布尔属性,让您清楚在给定方向上是否有更多记录。通过使用渲染属性中的表达式,您可以在页面上显示或隐藏该组件的结果。当您第一次加载页面时,上一页链接置灰,但如果您通过单击下一页链接向前移动时,上一个链接则变为活动状态。
每页选择菜单记录时使用您之前用到的 <apex:selectList>
,但我们没有通过调用控制器方法来获取菜单值,而是简单地使用 <apex:selectOption>
元素获取所需的值。每次选定的值发生变化时,<apex:actionSupport>
标记都会再次触发菜单,并再次使用 reRender="contacts_list"
来实现只更新 <apex:pageBlock>
的效果。这里不同的是,使用了 <apex:selectList>
设置标准列表控制器的 PageSize
属性。
了解详细信息...
标准列表控制器提供了诸多 Web 应用程序中常见的功能,远不止本章节介绍的功能。
例如,除了每次向前或向后移动一个页面的 Previous
和 Next
操作以外,还有指向记录列表开头或结尾的 First
和 Last
操作。
标记背后的场景是基于 StandardSetController
Apex 类的标准列表控制器。您可以通过 Lightning 平台 Apex 代码开发人员指南了解标准列表控制器的更多信息及其提供的功能。
这里创建的示例有一个小问题,示例名称会进行排序。通常希望列表有默认的排序顺序,还希望有影响排序的列标题,以便动态更改排序顺序。而事实是仅使用 Visualforce 是无法影响排序顺序的。尽管支持排序和可单击列标题所需的额外 Visualforce 标记和 Apex 代码数量不多,但确实需要自定义代码。初学阶段请多参阅其他资源。
资源
-
Visualforce 开发人员指南:标准控制器
-
Visualforce 开发人员指南:标准列表控制器
-
Visualforce 开发人员指南:在 Salesforce Classic 中创建自定义列表视图
-
Visualforce 开发人员指南:apex:outputLink 组件
-
Visualforce 开发人员指南: apex:repeat 组件
-
Apex 参考指南:StandardController 类
-
Apex 参考指南:StandardSetController 类
-
Salesforce 开发人员博客:Twitter Bootstrap 和 Visualforce 的简短介绍