Oracle APEX 中的流程控制
(在其他编程学科和环境中考虑这一点实际上很有用。)
问题定义
问题是页面项 (:P4550_REQUESTOR) 在满足条件之前不会填充值。看起来 PL/SQL 块进程在页面加载后立即将变量绑定到一个空值,尽管该进程在单击特定按钮之前不会触发。
问题陈述用 Apex 术语改写并以实际问题的形式呈现:
- 页面上有一个
REPORT REGION,其中包含直接引用数据表/视图的结果。此报告由称为“自动获取”的 Apex 进程管理,并通过加载页眉自动启动。
- 页面上有一个
FORM ITEM,由用户选择的BUTTON ITEM 有条件地填充。 BUTTON ITEM 是报告结果的一部分。
- 有多个按钮项。每个都与每个报告记录的值相关联。
- 如果用户没有从
REPORT REGION 中选择BUTTON ITEM,FORM ITEM 将保持未分配状态并包含“null”值。
有一个已定义的 PL/SQL 代码块设置为在按下 SUBMIT BUTTON 项目时执行(也在同一页面上)。为什么我的代码块(定义的页面进程)在没有先按REPORT REGION 中的BUTTON ITEM 的情况下触发时以空值运行?
面向程序程序员的事件驱动程序设计
如果您在程序语言的范式下思考,答案并不明显。在不深入讨论该主题的情况下,这是我制作的 OP 问题空间的可视化布局,以说明如何使问题更加明显:
这是我正在实施的 Apex 页面设计。它足够通用,可以用作其他 Apex 设计的模板。此图上没有流向箭头,因为它是一个有状态的系统。一件事导致另一件事发生,依此类推……但并非总是如此,也不是同时发生。
Apex UI 页面设计用例
尝试浏览几个用例,以了解图中分解的元素如何协同工作。每个用户可能会进行任意数量的点击组合和交互,但有一个共性:
- 它们都在页面加载时输入相同的初始化条件。
- 他们都通过以下方式离开页面:导航到其他地方或通过 SUBMIT 按钮事件。
用例 #1
- 用户从
{MyPage:SQLReport} 中的一条记录中选择{MyPage:SQLReport:ThisButton}
- 根据
{MyPage:SQLReport:ThisButton} #3,将报表记录与按钮项关联的值传递给:{MyPage:HTML-Region:ThisItem}
- 表单项状态已更新并从初始空值更改。
- 用户选择
{MyPage:HTML-Region:ThisSubmit}按钮通知系统继续。
- 提交按钮执行定义的 PL/SQL 过程块:
{MyPage:RunCodeBlock}
用例 #2
- 用户进入页面并查看
{MyPage:SQLReport}区域中显示的结果。
- 用户决定不需要额外输入,然后选择
{MyPage:HTML-Region:ThisSubmit} 按钮通知系统继续。
- (注意:此时表单项
{MyPage:HTML-Region:ThisItem}的状态还没有从最初的空值改变...在选择提交按钮之后)
- 提交按钮执行定义的 PL/SQL 过程块:
{MyPage:RunCodeBlock}
用例 #3
- 用户从
{MyPage:SQLReport} 中的一条记录中选择{MyPage:SQLReport:ThisButton}
- 根据
{MyPage:SQLReport:ThisButton} #3,将报表记录与按钮项关联的值传递给:{MyPage:HTML-Region:ThisItem}
- 表单项状态已更新并从初始空值更改。
- 用户从
{MyPage:SQLReport} 中的一条记录的不同选择中选择{MyPage:SQLReport:ThisButton}。
- 根据
{MyPage:SQLReport:ThisButton} #3,将报表记录与按钮项关联的值传递给:{MyPage:HTML-Region:ThisItem}
- 表单项状态已从步骤 (2) 中存储的初始值更新和更改。
- 用户选择
{MyPage:HTML-Region:ThisSubmit}按钮通知系统继续。
- 提交按钮执行定义的 PL/SQL 过程块:
{MyPage:RunCodeBlock}
每个案例之间的差异应该说明为什么依赖值(ThisItem,或者更具体地说,页面项目P4550_REQUESTOR)在一个用例与另一个用例中为空。
构建物理实现(Apex 页面)
我使用的表称为 STAR_EMPS。它类似于 EMP 表,但只有三列:ename、deptno 和salary。虽然不是很重要,但这是我用来填充 STAR_EMPS 的数据集:
我使用了一个名为 STAR_EMPS_LOG 的简单两列表来捕获成功执行的过程调用的输出。你可以只用一列来完成同样的事情,但我想要一个连续的 id 来跟踪每个事件的记录顺序——用于运行多个测试用例。该过程是此页面上保留的几个已定义过程之一:
包含在:{MyPage:RunCodeBlock} 下面:
DECLARE
-- output from this procedure will be recorded in the star_emps_log
-- table. {MyPage:RunCodeBlock}
mycelebrity star_emps.ename%TYPE:= :P17_CELEBRITY_NAME;
mylogmessage star_emps_log.log_message%TYPE;
BEGIN
-- Conditional; changes message based on the value set for the
-- page item.
if mycelebrity is null then
mylogmessage:= 'No button was pressed on the previous page.';
else
mylogmessage:= 'The user selected: ' || mycelebrity ||
' from the report list.';
end if;
-- populate value from the page item.
INSERT INTO star_emps_log (log_message)
VALUES (mylogmessage);
commit;
END;
页面布局是这样设置的:
- 与您的示例一样,我创建了一个带有支持元素的 {MyPage:SQLReport} 区域。 SQL 报告表示针对源数据表的查询。
- {MyPage:Form} 已重命名为 {MyPage:HTML-Region}。
- {MyPage:SQLReport} 由 SQL 查询定义,还有一个模拟列用作放置“编辑”按钮的占位符。
- {MyPage:SQLReport:ThisButton} 按钮规格详述如下:
两个页面进程:PROCESS 和 BRANCH 需要与引用 BUTTON 触发项的相同设置链接。
用户界面测试用例
浏览三个建议的场景以开始使用。验证系统是否正确解释了请求。这是页面布局的样子:
系统上的两个进程有一个在之前的讨论中没有提到的定义,可以解决我们手头的原始问题:
一些离别的想法
这是一件好事,一旦被打破,这就是一个微不足道的案例。此处描述的图表方法应适用于不同复杂性的其他 Apex 应用程序。远离代码,锁定术语并尝试在没有实际代码的情况下描述系统和流程具有相当大的实用性。如果这种方法有助于解决您自己的 Oracle Apex 设计挑战,请务必分享任何故事。
前进!