本章把 APEX 应用的页面流和会话状态放在一起理解:前者决定用户如何从一个页面进入下一个页面,后者决定临时值如何在请求、页面和用户会话之间保存、使用和清理。
8 理解页面流与会话状态#
APEX 应用通常不是孤立页面的集合,而是一组由业务任务串起来的页面。用户通过导航菜单进入业务功能,通过导航栏访问应用选项,再通过分支、重定向、面包屑、向导进度、模态对话框和抽屉在页面之间移动。
页面流解决“用户下一步去哪里”的问题;会话状态解决“跨请求、跨页面哪些临时值仍然可用”的问题。本章用三步新员工入职向导和列表/编辑流程两条主线,把这两个概念连在一起。
8.1 导航到业务功能#
用户登录后希望看到自己可以执行的任务。Navigation Menu 列表项用于把业务功能组织成清晰入口,例如提交职位申请、录入工时卡、新员工入职、审批收件箱、查看休假余额和申请休假。
创建页面时可以让 Create Page 向导自动添加导航菜单项,也可以稍后在 Shared Components > Navigation Menu 中手工维护。菜单项只负责入口呈现和目标导航;真正的访问控制仍应交给授权方案。
8.2 导航栏中的应用选项#
Navigation Bar List 通常承载用户名下拉菜单中的应用级操作,例如 Settings、About this Application、Push Notification 和 Sign Out。Create Application 向导默认创建 Sign Out 条目,其他功能也可能自动补充相关入口。
可以把导航菜单理解为“业务任务入口”,把导航栏理解为“当前用户或应用级选项入口”。这种分工能让用户快速判断一个链接是开始业务操作,还是管理应用/账号状态。
8.3 配置向导页面流#
Wizard 是把复杂任务拆成多个有序步骤的 UI 模式。新员工入职示例把身份信息、岗位信息和薪酬信息拆成三个页面,用户可以前进、后退、取消,最后在 Finish 步骤确认完成。
APEX 向导页面流由多页、向导进度列表、页面按钮和分支组成。页面收集当前步骤的数据,共享列表定义步骤标题和顺序,分支把 Next、Previous、Finish 等按钮连接到目标页面。
8.3.1 创建多步骤向导#
Create Page 向导可以自动生成多页向导。官方示例创建 Onboard New Employee,包含第 43、44、45 页。每页都有向导进度区域、当前步骤页面项以及 Next、Previous、Finish、Cancel 等按钮。
创建向导时需要指定向导名称、步骤标签、页面编号和导航图标。创建完成后,APEX 打开第一页的 Page Designer,后续再由开发者为每个步骤补充具体页面项和业务处理。
8.3.2 在 Page Designer 中编辑多步骤向导#
向导生成初始页面后,需要在 Page Designer 中添加每一步的具体内容。示例第 44 页 Job Role 区域是 List region,使用 Wizard Container 模板选项和 Wizard Progress 列表模板显示步骤进度。
Previous、Close、Next 等按钮放在 Wizard Container 模板的槽位中。共享 List 组件提供步骤条目,页面区域负责把这些条目渲染为“列车站点”式进度。
8.3.3 共享列表定义向导页面#
向导步骤标题和顺序来自 Shared Components 中的 List。Onboard New Employee 列表包含第 43、44、45 页三个条目,List region 再用 Wizard Progress 模板显示为步骤进度。
这种结构让步骤定义集中维护。修改共享列表中的条目标签或顺序后,所有使用该列表的向导页面都会体现变化。
8.3.4 用分支连接页面以定义流程#
Branch 定义页面提交处理后应该导航到哪里。Page Designer 的 Processing 选项卡中可以创建分支,指定目标页面、传递参数,并通过 When Button Pressed 限定哪个按钮触发该分支。
示例中第 43 页 Next 指向第 44 页;第 44 页 Previous 指回第 43 页、Next 指向第 45 页;第 45 页 Previous 指回第 44 页、Finish 指向首页。Cancel 通常配置为重定向,不提交当前页面数据。
8.3.5 呈现、提交、分支、重复#
APEX 运行时根据页面和组件定义执行一个稳定循环:渲染页面、接收提交、运行计算/验证/处理、执行分支,然后渲染下一个页面。
理解这个生命周期后,才能判断逻辑应放在 Pre-Rendering、Validating、After Submit、Processing、Branches 还是 Dynamic Actions 中。
8.3.5.1 向导示例的页面生命周期#
用户点击 Onboard New Employee 导航项后,APEX 根据第 43 页定义渲染页面。渲染前会先执行 Rendering 选项卡 Pre-Rendering 阶段的计算和处理,然后把区域、模板、页面项、按钮、CSS 和 JavaScript 输出到浏览器。
用户输入身份信息并点击 Next 后,浏览器提交页面项值。APEX 按 Processing 选项卡中的顺序执行计算、验证、处理和分支,第 43 页的 Next 分支决定第 44 页显示为下一页。
8.3.5.2 在呈现或提交阶段计算值#
Computation 用于设置页面项或应用项。如果计算结果影响页面初始显示,应放在 Pre-Rendering 阶段;如果用于提交后的保存前处理,应放在 Processing 选项卡的 After Submit 阶段。
计算来源可以是静态值、另一个项的值、PL/SQL 表达式、单列 SQL 查询或用户偏好。多值项可把多行查询结果格式化为分隔值或 JSON Array。
8.3.5.3 验证提交的数据#
Validation 在保存前检查提交数据的正确性,位于 Processing 选项卡的 Validating 执行点。它可以检查单个页面项,也可以用声明式条件、PL/SQL、SQL 或系统记录表检查复杂规则。
如果任何验证失败,用户会看到配置的错误消息,APEX 不会执行 Submit 生命周期后续 Processing 阶段的处理。Interactive Grid 列验证需要设置 Editable Region 并按列名引用网格列。
8.3.5.4 在呈现或提交阶段执行自定义处理#
Page Process 可以放在 Rendering 的 Pre-Rendering 阶段,也可以放在 Processing 的 After Submit 或 Processing 阶段。Execute Code 处理类型适合写应用特有逻辑,原生处理类型适合工作流、邮件、通知、报表打印、数据装载、API 调用、用户偏好和表单/网格保存等常见任务。
官方示例在向导最后一页创建 Execute Code 处理,并把 When Button Pressed 设为 FINISH,只有用户确认完成时才把第 43、44、45 页收集的数据插入 EMP 表。
insert into emp(empno, ename, deptno, hiredate, job, sal, comm)
values (:P43_EMPNO, :P43_ENAME, :P44_DEPTNO,
apex_session_state.get_timestamp('P44_HIREDATE'),
:P45_JOB, :P45_SAL, :P45_COMM);
8.3.5.5 Invoke API 最佳实践#
Oracle 建议把大部分业务逻辑写在 PL/SQL 包过程和函数中,再从页面中调用。Invoke API 页面处理可以声明式调用包过程,Page Designer 会显示过程参数,并在可能时自动把同名页面项映射为参数值。
这样做比在页面中散落大量内联 PL/SQL 更容易测试、复用和维护。低代码开发者可以配置页面调用,由资深开发者维护数据库包中的业务服务。
create or replace package body hr_app as
procedure add_new_employee(
p_empno in emp.empno%type,
p_ename in emp.ename%type,
p_deptno in emp.deptno%type,
p_hiredate in emp.hiredate%type,
p_job in emp.job%type,
p_sal in emp.sal%type,
p_comm in emp.comm%type) is
begin
insert into emp(empno, ename, deptno, hiredate, job, sal, comm)
values (p_empno, p_ename, p_deptno, p_hiredate, p_job, p_sal, p_comm);
end add_new_employee;
end hr_app;
8.3.5.6 让几乎任何元素具备条件#
几乎所有 APEX 页面元素都可配置 Server-side Condition,包括区域、页面项、列、按钮、验证、计算、处理和分支。条件为真时,元素才会渲染或执行。
按钮提交时,APEX 会把 REQUEST 设置为按钮名称。多个按钮共享逻辑时,可以使用 Request is contained in value,或用 PL/SQL 表达式精确列出允许的按钮名,避免 SAVE 与 SAVE_ADDRESS 这类包含关系导致误匹配。
:REQUEST in ('SAVE','UPDATE','INCREMENT')
:REQUEST in ('SAVE_ADDRESS','SAVE_ASSIGNMENT')
/* SAVE alone does not match */
8.4 其他页面流功能#
除了整页呈现、提交和分支,APEX 还支持后台数据请求、局部刷新和不提交数据的重定向。Breadcrumb 帮助用户理解钻取路径,Modal Dialog 和 Drawer 则能在保留调用页上下文的同时处理相关信息。
这些能力的共同目标是让页面流更自然:能局部刷新时不整页刷新,能保留上下文时不强制用户离开当前页面,能显示层级路径时不让用户迷路。
8.4.1 理解 AJAX 与重定向#
AJAX 请求让页面在后台与服务器交换数据,不刷新整页。APEX 常见 AJAX 场景包括 Execute Server-side Code 动态操作和 Refresh 区域刷新;Items to Submit、Items to Return 和 Page Items to Submit 决定请求携带和返回哪些值。
Redirect 是导航到另一个 URL,可传递参数,但不会提交当前页面改动,因此不会更新当前页面项的会话状态。若存在未保存更改,APEX 默认会给出警告,可通过 Warn on Unsaved Changes 控制。
8.4.2 用面包屑可视化钻取路径#
用户从列表钻取到明细,再进入相关记录时,Breadcrumb 可以显示当前所在层级并提供返回路径。官方示例从 Manage Employees 报表页进入 Employee 表单页,再进入 Dependents 列表页,最后进入 Dependent 表单页。
Breadcrumb 条目的短名称可使用页面项替换字符串,例如 &P6_ENAME. 或 &P9_NAME.。为了在后续页面继续显示正确名称,相关页面项需要使用 Per Session (Persistent) 存储,例如 P6_ENAME 和 P6_EMPNO。
8.4.3 模态对话框与抽屉#
Modal Dialog 页面显示在调用页上方,打开时用户不能操作底层页面;关闭或取消后,调用页恢复活动。若 Dialog Template 设为 Drawer,同一模态页面会从窗口边缘滑出,并占据窗口全高。
关闭对话框可以使用 Close Dialog 动态操作或 Close Dialog 页面处理,并可返回页面项值给调用页。调用页可监听 Dialog Closed 事件,读取返回项并刷新相关区域。Chained 属性决定模态页面再打开模态页面时复用窗口还是叠加窗口。
8.5 理解会话状态管理#
有些提交会立即保存到业务表;另一些场景则需要先临时保存,等用户完成流程后再统一落库。APEX 把每个用户会话中的页面项、应用项、集合和上传文件等临时值统称为 session state。
理解会话状态的关键是生命周期:值属于哪个用户会话,在哪个请求中产生,是否跨页面保留,何时清理,最终是否需要写入系统记录表。
8.5.1 临时存储的使用场景#
临时存储常用于四类场景:多页面任务、查询示例搜索、临时数据集合和文件上传。多页面任务需要让用户输入跨步骤保留;搜索条件常需要在会话内保持粘性;购物车等集合在结账前只是临时数据;上传附件在业务流程完成前通常先保存在临时区。
这些值不应全部直接写入业务表。应先判断它们是当前请求临时值、当前会话临时值、跨会话用户偏好,还是已经确认的业务事实。
8.5.2 临时值的会话上下文#
用户会话从用户访问并登录应用开始,到退出或空闲超时结束。同一时间可能有多个用户使用同一应用,APEX 为每个用户分配唯一 session ID。
这个 session ID 为用户交互和 session state 提供隔离上下文。两个用户使用同名页面项,也只能看到各自会话中的值。
8.5.3 APEX 如何管理用户会话状态#
三页入职向导要求用户在 Next 和 Previous 之间不丢失输入,但只有点击 Finish 时才插入 EMP 表。做法是把需要跨步骤保留的页面项设置为 Per Session (Persistent)。
页面渲染时,APEX 使用 session state 中的持久页面项值;页面提交完成后,它保存当前页中 Per Session 项的值,再执行分支或重新渲染。Per Request (Memory Only) 项则在请求结束后被忘记,除非处理过程保存或通过参数传递。
8.5.4 用户特定的临时存储#
若页面项值需要跨多个会话保留,可把 Session State > Storage 设为 Per User (Persistent)。这适合查询示例条件、用户选项等希望下次访问仍保持的偏好。
APEX_UTIL 包也提供 SET_PREFERENCE 和 GET_PREFERENCE,但对页面项而言,直接使用 Per User (Persistent) 是更简单的声明式方式。
8.5.5 清除会话状态#
会话结束时,APEX 会清理页面项、集合和临时文件等 session state;结束方式包括用户退出和空闲超时。会话期间也可以声明式清理指定状态。
常见方法有两种:在目标链接的 Clear Cache 中列出页面或集合,或在提交处理结束时使用 Clear Session State 页面处理。
8.5.5.1 用链接目标设置清除状态#
定义指向页面的链接时,可以在 Clear Cache 中列出一个或多个页面编号,清除这些页面的 session state。官方示例把 Onboard New Employee 菜单项设为 43,44,45,保证每次开始新向导都是干净状态。
Clear Cache 还可列出集合名称,或使用 RP 清除相关页面的报表分页。相同设置也可在按钮或链接的 Link Builder 对话框中配置。
8.5.5.2 用页面处理清除状态#
若需要在提交成功后再清理状态,可以使用 Clear Session State 原生页面处理。官方示例在向导最后一页配置该处理,FINISH 按钮成功插入新员工后清除第 43、44、45 页的页面项状态。
这种方式比进入页面前清理更适合“成功才清除”的流程,因为保存失败时用户仍可修正输入并重试。
8.5.6 应用项#
Application Item 是在用户会话期间保持的命名值。典型用法是 USER_ID:用户用用户名登录,但业务表引用 USERS.ID,因此可在认证后计算 USER_ID,避免各页面重复按用户名查询主键。
应用项可在不同上下文中用 #USER_ID#、&USER_ID. 或 :USER_ID 引用。PL/SQL 也可以调用 APEX_SESSION_STATE.SET_VALUE 设置它的值。
select id
from users
where username = lower(:APP_USER)
8.5.7 使用临时集合#
APEX Collection 允许在当前用户会话中存储一组临时行。运行时通过 APEX_COLLECTION 包创建命名集合;一个会话可以有多个集合,会话结束时 APEX 自动清理。
集合适合购物车、向导暂存、批量上传预览和复杂页面编辑等场景。它不是长期业务表,流程完成后应把确认数据写入正式表。
8.5.7.1 集合关键概念#
集合不需要预先定义结构,而是使用固定通用列:c001-c050 存 VARCHAR2,n001-n005 存 NUMBER,d001-d005 存 DATE,另有 clob001、blob001 和 xmltype001。每个成员都有 SEQ_ID,可用于更新或删除。
查询集合时使用 APEX_COLLECTIONS 视图,并以 collection_name 过滤。APEX 会按会话隔离集合数据,因此同名集合在不同用户会话中互不干扰。
| 数据类型 | 列名 | 数量 |
|---|---|---|
| VARCHAR2 | c001-c050 | 50 |
| NUMBER | n001-n005 | 5 |
| DATE | d001-d005 | 5 |
| CLOB | clob001 | 1 |
| BLOB | blob001 | 1 |
| XMLTYPE | xmltype001 | 1 |
apex_collection.create_collection(p_collection_name => 'SHOPPING_CART');
apex_collection.add_member(
p_collection_name => 'SHOPPING_CART',
p_c001 => 'COMP-APPL-MBP-16',
p_n001 => 2,
p_d001 => date'2025-05-27');
select c001 as item_code,
n001 as quantity,
d001 as need_by_date
from apex_collections
where collection_name = 'SHOPPING_CART';
8.5.7.2 可维护集合使用建议#
为了让页面不直接依赖 c001、n001 这类通用列名,可以为集合查询创建视图,把列映射为 item_code、quantity、need_by_date 等业务名称。
更进一步,可以用 PL/SQL 包封装集合操作,例如 clear_cart、add_item、remove_item、update_item。页面调用业务 API,包内部负责确保集合存在、生成稳定 ID,并使用 APEX_COLLECTION 添加、删除和更新成员。
create view shopping_cart_v as
select c001 as item_code,
c002 as id,
n001 as quantity,
d001 as need_by_date
from apex_collections
where collection_name = 'SHOPPING_CART';
8.5.8 临时文件#
File Upload 和 Image Upload 页面项都可把 Storage Type 设为 Table APEX_APPLICATION_TEMP_FILES。若 Purge File at 为 End of Session,上传文件会在当前会话期间保存在 APEX_APPLICATION_TEMP_FILES 表中。
NAME 列唯一标识临时文件,不同于用户上传的 FILENAME。单文件项可直接按页面项值查询;多文件项的值是冒号分隔的 NAME 列表,需要拆分后查询。
select blob_content, mime_type, filename
from apex_application_temp_files
where name = :P1_SINGLE_FILE_UPLOAD;
select blob_content, mime_type, filename
from apex_application_temp_files
where name in (
select column_value
from apex_string.split(:P1_MULTI_FILE_UPLOAD, ':')
);
8.5.9 检查会话状态以调试#
从 App Builder 运行应用时,Developer Toolbar 提供 Session > View Session State。打开后可以查看当前用户会话中的页面项、应用项、集合和后台执行。
这是排查“值为什么为空”“为什么旧值还在”“为什么当前页面上下文不对”的首选入口。调试时应看服务器端 session state,而不是只看浏览器输入框。
8.6 研究列表与编辑流程#
Create Page 向导可为任意数据源快速生成列表页和编辑页。理解生成结果如何协同,有助于后续定制,也有助于从空白页手工构建同类模式。
核心结构是:列表页负责查找和选择行,编辑页负责查看、创建、更新或删除行,二者通过链接参数、表单初始化、DML 处理和对话框关闭事件连接。
8.6.1 创建列表页与编辑页#
Interactive Report 的 Create Page 流程提供 Include Form Page 开关。启用后,向导会创建一个列表页和一个配套表单页。官方示例创建 Manage Employees 报表第 3 页,同时生成 Employee 表单第 4 页。
默认 Form Page Mode 是 Modal Drawer,从侧边滑出覆盖在列表页上。创建时还可配置 Breadcrumb、导航菜单条目和 fa-users-alt 图标,并确认 EMPNO 为主键。
8.6.2 运行列表页与编辑页#
运行应用后,导航菜单出现 Manage Employees 条目。进入页面后,Interactive Report 显示员工列表和 Create 按钮;从 App Builder 运行时,Developer Toolbar 也会显示在页面底部。
点击某行编辑图标后,模态抽屉从右侧滑出,允许编辑或删除所选员工。Developer Toolbar 可启用 Auto Hide,让工具栏平时更少遮挡页面。
8.6.3 链接到编辑页#
向导把 Interactive Report 的 Link Column 配置为 Link to Custom Target,目标是新建表单页第 4 页。在 Set Items 中,目标页主键项 P4_EMPNO 接收当前报表行的 #EMPNO# 值。
Link Icon 使用 Font APEX 图标 HTML,并通过 aria-label 和 title 支持可访问性与悬停提示。点击图标后,用户被重定向到模态抽屉页,并把当前 EMP 行主键传给表单页。
8.6.4 按主键取回已有行#
表单页在 Pre-Rendering 的 Before Header 阶段有 Form - Initialization 页面处理,用于按主键取回要编辑的行。该处理通过关联 Form Region 识别数据源和主键列。
如果 P4_EMPNO 在页面渲染时有值,初始化处理会查询 EMP 并填充表单项;如果主键为空,则不查询,表单保持空白,适合新增记录。
select ename, mgr, hiredate, sal, comm, deptno
into :P4_ENAME, :P4_MGR, :P4_HIREDATE, :P4_SAL, :P4_COMM, :P4_DEPTNO
from emp
where empno = :P4_EMPNO;
8.6.5 表单页面项使用 Memory Only 存储#
向导生成的表单区域页面项默认使用 Per Request (Memory Only)。请求处理结束后,APEX 不会把这些值保存在用户 session state 中。
这是合理的,因为表单提交会立即通过 DML 处理保存到 EMP 表;关闭对话框后,不需要让这些字段继续留在 session state。Create 按钮不传 P4_EMPNO,因此新增表单打开时为空。
8.6.6 用页面处理保存更改#
表单页生成的 Process form Employee 使用 Form - Automatic Row Processing (DML)。提交按钮的 Database Action 决定执行 INSERT、UPDATE 或 DELETE;如果按钮没有 Database Action,该处理不执行 DML。
该处理关联表单区域,默认支持对区域源执行 DML、防止丢失更新、锁定被更新或删除的行,并在插入后把主键返回到对应页面项。
8.6.7 条件化数据库动作按钮#
向导会为 CREATE、SAVE、DELETE、CANCEL 配置不同运行时行为。CREATE 在 P4_EMPNO 为空时显示并执行 INSERT;SAVE 和 DELETE 在 P4_EMPNO 非空时显示,分别执行 UPDATE 和 DELETE;CANCEL 通常使用动态操作关闭对话框。
DELETE 按钮会显示确认消息并使用 Danger 样式。消息可使用 &APP_TEXT$DELETE_MSG!RAW. 这样的可翻译文本键,!RAW 允许消息包含 HTML。普通编辑页中的 Cancel 也可能通过 CANCEL 分支返回列表页。
| 按钮 | 行为 | 执行验证 | DB Action | 服务器端条件 |
|---|---|---|---|---|
| CREATE | Submit Page | Yes | INSERT | P4_EMPNO is null |
| SAVE | Submit Page | Yes | UPDATE | P4_EMPNO is not null |
| DELETE | Submit Page | No | DELETE | P4_EMPNO is not null |
| CANCEL | Dynamic Action | No |
8.6.8 对话框关闭后刷新列表#
表单页 DML 处理之后,向导会配置 Close Dialog 页面处理。因为 Create、Apply Changes 和 Delete 都应关闭对话框,所以该处理通常用 REQUEST 条件限定 CREATE、SAVE 或 DELETE。
对话框关闭意味着父列表中的数据可能已变化。向导会在列表页创建 Dialog Closed 动态操作,监听由 Manage Employees 区域打开的对话框关闭事件,然后执行 Refresh 动态操作刷新 Interactive Report 区域。