主从页面用于处理一对多关系中的两组数据,例如部门与员工、订单与订单行。APEX 的创建页面向导(Create Page Wizard)可以生成堆叠网格、左右并排选择、钻取编辑三类页面;学习本章时,重点不是记住向导名称,而是判断用户到底需要批量维护、快速浏览,还是进入单条主记录集中编辑。
官方来源:10 Working with Master/Detail Data
10.1 主从向导模式#
企业应用经常围绕一对多关系建模。以 EMP/DEPT 示例数据为例,一个部门可以包含多名员工,一个员工也可能管理多名员工。这类关系在业务系统中可能被称为表头/行(Header/Lines)、父/子(Parent/Child)或主/从(Master/Detail)。
在 App Builder 的创建页面向导中,选择主从页面模式后,需要指定主表、明细表以及二者之间的关系。向导提供三种常用入口:
- Stacked(堆叠):同一个页面中放置主表和明细表两个可编辑交互式网格(Interactive Grid),适合批量修改多个主记录及其明细。
- Side by Side(左右并排):左侧使用紧凑列表选择主记录,右侧显示主记录的更多字段和相关明细,适合浏览、查找和轻量维护。
- Drill Down(钻取):先在列表页选择主记录,再进入编辑页同时维护主记录和明细行,适合围绕一条主记录完成较完整的编辑任务。
学习路径#
- 前置条件:准备一个存在主键/外键关系的数据模型,例如
DEPT与EMP。 - APEX 位置:App Builder > 应用 > Create Page > Master Detail。
- 操作:分别查看 Stacked、Side by Side、Drill Down 三个模式的说明,确认每个模式要求选择的主表、明细表和关系字段。
- 验证点:能解释为什么同样的
DEPT/EMP关系可以生成三种不同页面,而不是只存在一种标准主从页面。 - 预期结果:根据用户任务选择模式:批量录入选 Stacked,快速浏览选 Side by Side,单条主记录深度编辑选 Drill Down。
官方来源:10.1 Reviewing Master/Detail Wizard Patterns
10.2 配置主从网格#
选择 Stacked 模式后,APEX 会生成两个彼此联动的可编辑交互式网格。主网格当前选中哪一行,明细网格就只显示外键指向该主键的明细行。这个联动不是依靠开发者手写明细查询条件完成的,而是由明细网格上的主从属性驱动。
体验堆叠主从网格的运行行为#
页面中有两个 Interactive Grid 工具栏,各自可以添加行,但网格自身不单独显示保存按钮。用户可以在多个部门和多个员工之间切换、添加、修改或删除数据,最后通过页面级 Save(保存) 按钮一次提交。这样主表和明细表的变更会进入同一个页面提交事务;如果校验失败,相关修改不会部分保存。
交互式网格会在客户端跟踪新增、修改和删除的行。用户甚至可以先新增一个部门,例如 DESIGN,再为这个尚未提交的新部门新增多名员工,然后继续切换其他部门补充修改,最后一次保存。
- 前置条件:主表和明细表存在明确主键/外键关系,示例中为
DEPT.DEPTNO与EMP.DEPTNO。 - APEX 位置:运行 Stacked Master/Detail 页面,观察两个 Interactive Grid 区域。
- 操作:切换主网格行,确认明细网格刷新;新增一个主行并新增明细行;点击页面级 Save。
- 验证点:明细网格只显示当前主行的员工;新增明细能正确挂到新增部门;校验失败时不应出现主从数据部分提交。
- 预期结果:用户可以把一组相关主从变更作为一个业务动作保存。
理解驱动主从网格的两个属性#
明细网格至少需要两个配置点。第一,在明细网格区域上设置 Master Region(主区域),选择它应该跟随哪个主网格的选中行。配置好后,通常不需要为了主从联动再写一段 Where Clause;如果业务还需要额外过滤,可以再添加独立条件。
第二,在明细网格的外键列上设置 Master Column(主列)。例如选择 Employees 网格中的 DEPTNO 列,把它的 Master Column 指向主区域中的 DEPTNO。如果关系是复合键,则需要为多个明细列分别指定对应的主列。
- APEX 位置:Page Designer > 明细 Interactive Grid 区域;Page Designer > 明细网格外键列。
- 操作:检查明细区域的 Master Region;检查外键列的 Master Column;移除不必要的主从 Where Clause。
- 验证点:切换主网格选中行时,明细网格自动只显示相关行;新增明细行时外键值能跟随当前主行。
- 预期结果:页面通过声明式属性维持主从一致性,减少手写过滤和保存逻辑。
官方来源:10.2 Configuring Master/Detail Grids
10.3 并排显示主从数据#
Side by Side 模式把主记录选择和明细查看放在同一页的左右两侧。左侧是可搜索的紧凑主记录列表,右侧显示当前选中主记录的完整字段以及一个或多个明细区域。它适合“先找对象,再看详情”的工作流。
页面结构:左侧选择,右侧展示#
向导会要求为主表指定 Primary Display Column(主要显示列) 和 Secondary Display Column(次要显示列)。用户在左侧列表中看到这两个值,可以搜索它们,并点击某一行来更新右侧内容。
该页面使用 Left Side Column(左侧列) 页面模板。左侧列中通常包含一个 Search 静态内容区域、一个搜索项(示例为 P4_SEARCH),以及名为 Master Records 的 Classic Report。主体区域中,主记录 Departments Classic Report 使用 Value Attribute Pairs - Column 模板显示标签/值;明细 Employees Classic Report 使用 Standard 模板显示表格数据。
页面上的 Create 按钮通常跳转到 Departments 模态抽屉表单,用于新增主记录;明细区域中的加号按钮跳转到 Employees 模态抽屉表单,用于给当前部门新增员工。如果向导中选择了更多明细表,它们会作为额外明细区域出现在主体区域中。
搜索主记录并刷新列表#
搜索项 P4_SEARCH 通过 Key Press 事件上的动态操作响应用户按下 Enter。动态操作的客户端条件只允许 Enter 键触发:
this.browserEvent.which === apex.jQuery.ui.keyCode.ENTER
该动态操作包含两个动作:刷新 Master Records 区域,然后执行 Cancel Event,避免浏览器把单文本框表单的 Enter 键解释为整页提交。
Master Records 区域查询在 WHERE 子句中使用 :P4_SEARCH 匹配 DNAME 和 LOC,并且需要在区域的 Page Items to Submit(要提交的页面项) 中填写 P4_SEARCH,这样刷新区域时服务器才能拿到最新搜索值。
select "DEPTNO",
...
from "DEPT" x
where (:P4_SEARCH is null
or upper(x."DNAME") like '%' || upper(:P4_SEARCH) || '%'
or upper(x."LOC") like '%' || upper(:P4_SEARCH) || '%')
order by "DNAME"
- APEX 位置:Page Designer >
P4_SEARCH;Dynamic Actions > Perform Search;Master Records 区域属性。 - 操作:设置 Enter 条件;刷新 Master Records;取消默认事件;在区域属性中提交
P4_SEARCH。 - 验证点:输入部门名称或地点并按 Enter 后,左侧列表刷新;页面不发生意外整页提交。
- 预期结果:用户可以在左侧快速缩小主记录范围,并保留并排查看体验。
用 Media List 模板呈现主记录#
Master Records Classic Report 使用 Media List 报表模板,把主记录的主要字段和次要字段格式化成可点击列表项。需要理解模板时,可以进入 Shared Components > Templates 搜索 Media List。它是 Named Column 类型的 Report 模板,行模板通过列名替换字符串取得数据。
Named Column 模板要求查询列名与模板替换名匹配。这个页面的查询从 DEPT 表取数,但会额外生成 LINK、LIST_CLASS、LIST_TITLE 和 LIST_TEXT 等列。LINK 使用 apex_page.get_url 回到当前页并传入 P4_DEPTNO;LIST_CLASS 在当前行等于已选部门时返回 is-active,用于高亮左侧选中项。
apex_page.get_url(
p_items => 'P4_DEPTNO',
p_values => "DEPTNO") as LINK,
case
when coalesce(:P4_DEPTNO, '0') = "DEPTNO" then 'is-active'
else null
end as LIST_CLASS,
substr("DNAME", 1, 50) as LIST_TITLE,
substr("LOC", 1, 50) as LIST_TEXT
跟踪当前选中的主记录#
页面使用隐藏项 P4_DEPTNO 保存当前选中部门的主键。它的 Session State > Storage 设置为 Per Session (Persistent),因此同一用户会话中可以持续知道当前选择。用户点击左侧某个 Master Records 链接后,链接把该行 DEPTNO 传给当前页的 P4_DEPTNO,页面重新渲染,右侧主记录详情和明细记录随之更新。
右侧 Departments 区域通过 DEPTNO = :P4_DEPTNO 只显示当前主记录,并设置服务器端条件:当 :P4_DEPTNO 为空时隐藏区域。该区域使用 Value Attribute Pairs - Column 模板,以标签/值形式显示单条主记录,分页设置为 No Pagination。
明细 Employees Classic Report 使用 Standard 模板,并通过 DEPTNO = :P4_DEPTNO 过滤 EMP.DEPTNO 外键列。这样同一个隐藏项同时驱动主记录详情区域和明细区域。
- APEX 位置:Page Items >
P4_DEPTNO;Master Records 查询;Departments 与 Employees Classic Report 区域。 - 操作:配置左侧链接给
P4_DEPTNO赋值;主记录和明细区域都引用该隐藏项过滤;主记录区域为空时隐藏。 - 验证点:点击不同部门后,左侧高亮、右侧标签/值详情、员工表格三处同时变化。
- 预期结果:用户始终清楚当前正在查看哪个主记录,明细数据不会串到其他主记录下。
创建与编辑主记录、明细记录#
面包屑栏中的 Create 按钮跳转到 Departments 表单页,不传主键值时用于新增部门。Departments 区域中的 Edit 按钮跳转到同一个模态抽屉表单页,但会传入当前主记录的主键,因此打开的是编辑状态。
Employees 明细区域中的加号按钮跳转到员工模态抽屉表单。链接目标会把当前页面的 P4_DEPTNO 传给目标页上的外键项,例如 P7_DEPTNO,所以新增员工表单打开时已经带有当前部门编号。
向导默认通常只配置新增明细,不一定配置编辑明细。要让用户点击员工姓名编辑员工,可以复用向导生成的员工表单页:把 ENAME 列类型从 Plain Text 改为 Link,链接目标指向员工表单页,并把当前明细行的主键传给目标页的主键项。
- APEX 位置:Buttons > Create/Edit;Employees 报表列
ENAME;目标模态表单页面项。 - 操作:检查主记录创建/编辑按钮目标;检查明细新增按钮传参;必要时把明细字段改为链接以支持编辑。
- 验证点:新增员工时外键默认等于当前部门;点击员工姓名能打开对应员工的编辑表单,而不是空白新增表单。
- 预期结果:并排页面承担浏览和选择,模态抽屉承担主记录或明细记录的具体编辑。
官方来源:10.3 Showing Master/Detail Data Side by Side
10.4 钻取编辑主记录和明细#
Drill Down 模式把“查找主记录”和“编辑主记录及其明细”分成两个页面。列表页让用户定位主记录,编辑页则围绕当前主记录提供表单和明细网格,适合需要集中完成一条主记录下多项修改的场景。
钻取模式生成的页面#
向导会生成一个显示主记录的 Interactive Report。每条主记录旁有编辑图标,用户点击后进入编辑页。
编辑页包含主记录 Form 区域和可编辑的明细 Interactive Grid。示例中用户可以在同一页编辑当前 Department,例如 ACCOUNTING,并维护该部门的 Employees。明细网格自身的 Save 工具栏按钮被关闭,页面级 Apply Changes(应用更改) 按钮负责提交主记录和明细记录,保证它们一起保存。
编辑页还可以提供上一条/下一条导航。用户在当前主记录上修改表单和明细后,可以点击 Apply Changes 保存,也可以通过导航按钮保存并切换到其他主记录。
明细网格的最低配置#
编辑页上的明细网格以 EMP 表为数据源。如果不限制,它会显示所有员工。最关键的过滤条件是让 EMP.DEPTNO 等于当前页面中主记录表单项 P9_DEPTNO 的值:
DEPTNO = :P9_DEPTNO
因为查询引用了页面项,明细网格区域需要在 Page Items to Submit 中提交对应页面项。官方捕获文本在这一处写成 P9_EMPNO,但按上下文和过滤条件判断,应重点核对当前页面实际引用的主键项是否为 P9_DEPTNO,避免刷新明细时拿不到主记录编号。
另一个关键点是明细网格中的 DEPTNO 外键列。它应该默认取当前主记录页面项 P9_DEPTNO 的值。这样用户在编辑 ACCOUNTING 部门时新增员工,新员工会自动关联到部门编号 10。
- APEX 位置:编辑页 > Employees Interactive Grid 区域;Employees 网格列
DEPTNO。 - 操作:设置明细区域过滤条件;配置 Page Items to Submit;把外键列默认值设为当前主记录项。
- 验证点:进入某个部门编辑页时,只看到该部门员工;新增员工时
DEPTNO自动等于当前部门。 - 预期结果:明细网格始终围绕当前主记录工作,不会显示或保存到错误部门。
配置表单记录导航#
主表 Form 区域依靠 Pre-Rendering 中的 Form - Initialization 页面处理来按主键加载当前 DEPT 行。为了支持上一条/下一条导航,这个初始化处理还配置了三个导航相关属性:
- Next Primary Key Item(s):例如
P9_DEPTNO_NEXT。 - Previous Primary Key Item(s):例如
P9_DEPTNO_PREV。 - Current Row/Total Item:例如
P9_DEPTNO_COUNT。
Form 区域的 Order By Clause(排序子句) 决定上一条和下一条的顺序。页面渲染时,初始化处理计算下一条主键、上一条主键以及类似“3 of 4”的当前位置提示。上一条/下一条按钮通过服务器端条件判断隐藏项是否为空来决定是否显示;对应分支在按钮按下时把 P9_DEPTNO_NEXT 或 P9_DEPTNO_PREV 传回 P9_DEPTNO,从而重新渲染编辑页。
- APEX 位置:Pre-Rendering > Form - Initialization;Buttons;Branches;相关隐藏项和 Display Only 项。
- 操作:配置导航页面项;设置 Form 排序;给按钮和分支添加 When Button Pressed 条件。
- 验证点:第一条记录不显示 Previous,最后一条记录不显示 Next,中间记录能正确显示当前位置并切换。
- 预期结果:用户可以在编辑页连续处理多条主记录,而不必回到列表页重新选择。
一次创建新的主记录和多条明细#
向导默认会在 P9_DEPTNO is null 时隐藏 Employees 明细网格。因此用户从主记录列表点击 Create 时,通常只能先创建部门,保存后再回头编辑该部门并新增员工。要支持“一次创建部门和多个员工”,需要两个小改动。
第一,移除 Employees 明细网格上的服务器端条件,让创建主记录时也显示空明细网格。第二,在 Processing 中添加一个 Assign Deptno Foreign Key 页面处理,顺序必须放在 Process form Form on DEPT 之后、Employees - Save Interactive Grid Data 之前。这样主表表单处理先插入新部门并把生成的 DEPTNO 回写到 P9_DEPTNO,随后自定义处理再把这个值分配给新员工行的外键。
-- If Employee row being (C)reated, assign DEPTNO foreign key
if :APEX$ROW_STATUS = 'C' then
:DEPTNO := :P9_DEPTNO;
end if;
这个处理的 Editable Region(可编辑区域) 要设置为 Employees 网格。由于 Interactive Grid 可以一次保存多行,APEX 会对每一条被插入、更新或删除的网格行执行一次该处理;代码通过 APEX$ROW_STATUS = 'C' 确保只给新建员工行赋外键,更新和删除行不会被误改。
完成后,用户从 Departments Interactive Report 点击 Create,会看到空部门表单和空 Employees 网格。用户可以填写一个新部门,并在下方添加多名员工,点击 Create 后作为一个事务保存。
- APEX 位置:Employees 网格服务器端条件;Processing > Execute Code;Process form Form on DEPT;Employees - Save Interactive Grid Data。
- 操作:让创建模式显示明细网格;添加逐行 Execute Code;确认处理顺序;只在
APEX$ROW_STATUS = 'C'时赋外键。 - 验证点:新建部门时能同时录入员工;保存后所有员工的
DEPTNO都等于新生成的部门编号;更新或删除员工不会被该处理误赋值。 - 预期结果:用户把“创建主记录并补充明细”作为一个连续动作完成,减少来回跳转。