【问题标题】:Application Express: Anonymous PL/SQL Block and Bind VariablesApplication Express:匿名 PL/SQL 块和绑定变量
【发布时间】:2014-07-18 04:24:44
【问题描述】:

我在将页面项的值绑定到匿名 PL/SQL 块进程中声明的变量时遇到问题。

问题是页面项 (:P4550_REQUESTOR) 在满足条件之前不会填充值。看起来 PL/SQL 块进程在页面加载后立即将变量绑定到一个空值,尽管该进程在单击特定按钮之前不会触发。

这是我的代码:

DECLARE 

v_email_to   app_user.email%type;
v_requestor  VARCHAR2(15);

BEGIN

  v_requestor := :P4550_REQUESTOR;

BEGIN
    SELECT email INTO v_email_to
    FROM app_user 
    WHERE userid = v_requestor;
END;

SEND_APEX_MAIL (
                 v_email_to,

                 'Your vacancy request has been rejected.'
                 || chr(10)
                 || 'Emailed to: ' || v_email_to
                 || chr(10)
                 || 'Requestor: ' || v_requestor,

                 'Vacancy Request Rejected' 
               );

END; 

有人对此有什么想法吗?

如果我将值硬编码到 v_requestor,该块就可以正常工作。如果我在页面加载后尝试获取 P4550_REQUESTOR 的值,它是空的。单击编辑按钮后,将填充 P4550_REQUESTOR。

** **更多详情** **

P4550_REQUESTOR 是位于空缺请求区域内的页面项目,仅在满足条件时才会显示。具体来说,条件是与在页面加载时创建的表格行相关联的编辑按钮。单击编辑按钮会显示详细信息区域,并填充相关的页面项目。

Vacancy Request 区域中的页面项目值是通过在页眉之后触发的自动行提取填充的。

P4550_REQUESTOR 的源类型为 DB 列。

触发上述代码的进程设置为在提交时触发 - 在计算和验证之后

如果我在页面加载时记录 P4550_REQUESTOR 的值,它会显示为 null。如果我在单击编辑按钮后记录该值,我会得到预期的字符串值。

【问题讨论】:

  • 您似乎在说 :P4550_REQUESTORNULL 在执行此代码的时间点。如果您不认为这是正确的,您能否详细介绍一下 :P4550_REQUESTOR 是如何分配的以及该进程是如何配置运行的?与按下按钮时触发的进程相关联的代码通常不会在页面加载时运行。
  • 嗨,贾斯汀,我已经添加了有关该问题的一些详细信息。
  • 事实证明,页面项目没有将“保存会话状态”设置为“是”。这部分解决了问题,但是在运行自动行提取后,我仍然无法在会话状态中获取正确的值...
  • “保存会话状态”表示它保留自访问页面以来分配的最后一个值。获得“正确”值的问题就是因为这个。

标签: plsql oracle11g oracle-apex


【解决方案1】:

最初的冗长答案似乎使问题过于复杂。 session state concepts manual 更简洁地涵盖了这种行为。

如果 P4550_REQUESTOR 是从向导创建的普通项目,使用 :P4550_REQUESTOR 将在运行后提交的进程中返回一个值,因为提交进程将浏览器中的值移动到会话状态。

如果 P4550_REQUESTOR 有条件地渲染,那么它将始终为 null,我不确定如果您尝试设置它会发生什么 - 可能取决于如何设置。

类似说明,如果您使用了 &P4550_REQUESTOR。要参数化该过程,您将面临最初描述的行为(并使代码的安全性降低)

【讨论】:

    【解决方案2】:

    Oracle APEX 中的流程控制

    (在其他编程学科和环境中考虑这一点实际上很有用。)

    问题定义

    问题是页面项 (:P4550_REQUESTOR) 在满足条件之前不会填充值。看起来 PL/SQL 块进程在页面加载后立即将变量绑定到一个空值,尽管该进程在单击特定按钮之前不会触发。

    问题陈述用 Apex 术语改写并以实际问题的形式呈现:

    1. 页面上有一个REPORT REGION,其中包含直接引用数据表/视图的结果。此报告由称为“自动获取”的 Apex 进程管理,并通过加载页眉自动启动。
    2. 页面上有一个FORM ITEM,由用户选择的BUTTON ITEM 有条件地填充。 BUTTON ITEM 是报告结果的一部分。
    3. 有多个按钮项。每个都与每个报告记录的值相关联。
    4. 如果用户没有从REPORT REGION 中选择BUTTON ITEMFORM ITEM 将保持未分配状态并包含“null”值。

    有一个已定义的 PL/SQL 代码块设置为在按下 SUBMIT BUTTON 项目时执行(也在同一页面上)。为什么我的代码块(定义的页面进程)在没有先按REPORT REGION 中的BUTTON ITEM 的情况下触发时以空值运行?

    面向程序程序员的事件驱动程序设计

    如果您在程序语言的范式下思考,答案并不明显。在不深入讨论该主题的情况下,这是我制作的 OP 问题空间的可视化布局,以说明如何使问题更加明显:

    这是我正在实施的 Apex 页面设计。它足够通用,可以用作其他 Apex 设计的模板。此图上没有流向箭头,因为它是一个有状态的系统。一件事导致另一件事发生,依此类推……但并非总是如此,也不是同时发生。

    Apex UI 页面设计用例

    尝试浏览几个用例,以了解图中分解的元素如何协同工作。每个用户可能会进行任意数量的点击组合和交互,但有一个共性:

    1. 它们都在页面加载时输入相同的初始化条件。
    2. 他们都通过以下方式离开页面:导航到其他地方或通过 SUBMIT 按钮事件。

    用例 #1

    1. 用户从{MyPage:SQLReport} 中的一条记录中选择{MyPage:SQLReport:ThisButton}
    2. 根据{MyPage:SQLReport:ThisButton} #3,将报表记录与按钮项关联的值传递给:{MyPage:HTML-Region:ThisItem}
    3. 表单项状态已更新并从初始空值更改。
    4. 用户选择{MyPage:HTML-Region:ThisSubmit}按钮通知系统继续。
    5. 提交按钮执行定义的 PL/SQL 过程块:{MyPage:RunCodeBlock}

    用例 #2

    1. 用户进入页面并查看{MyPage:SQLReport}区域中显示的结果。
    2. 用户决定不需要额外输入,然后选择{MyPage:HTML-Region:ThisSubmit} 按钮通知系统继续。
    3. (注意:此时表单项{MyPage:HTML-Region:ThisItem}的状态还没有从最初的空值改变...在选择提交按钮之后)
    4. 提交按钮执行定义的 PL/SQL 过程块:{MyPage:RunCodeBlock}

    用例 #3

    1. 用户从{MyPage:SQLReport} 中的一条记录中选择{MyPage:SQLReport:ThisButton}
    2. 根据{MyPage:SQLReport:ThisButton} #3,将报表记录与按钮项关联的值传递给:{MyPage:HTML-Region:ThisItem}
    3. 表单项状态已更新并从初始空值更改。
    4. 用户从{MyPage:SQLReport} 中的一条记录的不同选择中选择{MyPage:SQLReport:ThisButton}
    5. 根据{MyPage:SQLReport:ThisButton} #3,将报表记录与按钮项关联的值传递给:{MyPage:HTML-Region:ThisItem}
    6. 表单项状态已从步骤 (2) 中存储的初始值更新和更改。
    7. 用户选择{MyPage:HTML-Region:ThisSubmit}按钮通知系统继续。
    8. 提交按钮执行定义的 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;
    

    页面布局是这样设置的:

    1. 与您的示例一样,我创建了一个带有支持元素的 {MyPage:SQLReport} 区域。 SQL 报告表示针对源数据表的查询。
    2. {MyPage:Form} 已重命名为 {MyPage:HTML-Region}。
    3. {MyPage:SQLReport} 由 SQL 查询定义,还有一个模拟列用作放置“编辑”按钮的占位符。
    4. {MyPage:SQLReport:ThisButton} 按钮规格详述如下:

    两个页面进程:PROCESS 和 BRANCH 需要与引用 BUTTON 触发项的相同设置链接。

    用户界面测试用例

    浏览三个建议的场景以开始使用。验证系统是否正确解释了请求。这是页面布局的样子:

    系统上的两个进程有一个在之前的讨论中没有提到的定义,可以解决我们手头的原始问题:

    一些离别的想法

    这是一件好事,一旦被打破,这就是一个微不足道的案例。此处描述的图表方法应适用于不同复杂性的其他 Apex 应用程序。远离代码,锁定术语并尝试在没有实际代码的情况下描述系统和流程具有相当大的实用性。如果这种方法有助于解决您自己的 Oracle Apex 设计挑战,请务必分享任何故事。

    前进!

    【讨论】:

    • 感谢您提供的图表。这绝对是一个有用的模板,可以鼓励我退后一步,看看应用程序/页面设计和相关部分的交互。关于我的页面设置与您的图表相关的一些注意事项。 1) MyPage:Report 由标准 SQL Query 处理; 2) MyPage:Form 实际上是 MyPage:HTML 区域。我面临的问题是,在按下 {MyPage:Report:ThisButton} 之后,我无法检索 {MyPage:HTML:ThisItem} 以在 {MyPage:RunCodeBlock} 中使用,即使该项目包含一个值。 “保存会话状态”解决了这个问题。
    • 所以,我的问题是,为什么?是否需要在会话状态中包含这些值才能在提交时触发的页面进程中使用?我对 APEX 还是比较陌生,因此试图了解其工作原理的复杂性。谢谢!
    • 我喜欢你的思路。继续问为什么!我什至感谢您使用我汇总的设计术语进行交流的努力。我使用我们讨论过的设计构建了一个单页示例。我将尝试解释“保存会话状态”及其工作原理,以及没有它如何完成相同的任务。
    • 感谢 Richard 的详尽反馈。非常感谢。
    猜你喜欢
    • 2013-11-07
    • 2014-09-30
    • 1970-01-01
    • 2017-09-02
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 2013-05-29
    相关资源
    最近更新 更多