使用 ADF Faces 对话框架 (ADF Faces EA15)

作者: Jonas Jacobi,甲骨文公司
2005 年 4 月
修订版 1.0

使用 ADF Faces 对话和流程框架

本文介绍了如何使用 ADF Faces 对话和流程框架。ADF Faces 对话和流程框架提供了一个简单方法,使您可以跳转到某个页面或一系列页面以从用户那里获取信息,然后返回到原始页面以使用该信息。在 ADF Faces 中,这称作“流程”。ADF Faces 支持的一个特殊类型的流程是“对话”:它显示在一个单独的弹出窗口中,而不是同一窗口中。ADF Faces 使您不用再管理启动这些窗口所需的 JavaScript,并封装了普通“流程”与“对话框”之间的差异;例如,它可以在客户端不支持打开新窗口(例如,PDA)的情况下切换为使用同一窗口。

我将使用一个要求用户登录以查看某些数据(他们的定单)的简单应用程序。该应用程序由五个页面组成 - login.jspx、 orders.jspx、 new_account.jspx、 account_details.jspx 以及最终的错误页面 - error.jspx。

图 1:示例应用程序的页面流

配置 Faces 以利用对话框架

首先,我们需要配置 JSF 项目,以使用 Oracle 的 ADF Faces 组件库提供的内置对话框架。要指定一个启动流程或对话的规则,只需指定一个命令组件输出和一个以“ process:”开头的导航规则。请看以下代码示例:

    <af:commandLink id="cmdLink" text="New user?"
  
                              
action="process:newAccount"
useDialog="true"/>
在登录页面中,我们使用了 commandLink 组件来启动“新帐户”流程,为此我们向操作输出中添加了“ process:”要使此“新帐户”页面出现在弹出窗口中,我们必须将 useDialog 属性设置为“ true”。此属性只与以“ process:”开头的输出搭配使用。如果操作输出中未使用“ process:”,则 useDialog=true 将不启动对话。反过来也同样适用,如果定义了“ process:”,但忽略了 useDialog 属性或设置为 false,则一个新流程将在同一窗口中作为“开始”页启动,当流程结束时,用户将返回到初始的开始页面。要充分利用对话框架,我们需要创建一个具有匹配 <from-outcome> 的导航规则:
  <navigation-rule>
<from-view-id>/login.jspx</from-view-id>
<navigation-case>
  
                              
<from-outcome>process:newAccount</from-outcome>
<to-view-id>/new_account.jspx</to-view-id> </navigation-case>


当用户单击“New User” commandLink 时,这将在对话中显示“ / new_account .jspx”页。注意,我们还将 commandLink 上的“ partialSubmit”设置为“ true”;强烈建议您在启动对话的按钮/链接上使用此选项,这是因为它可以避免在对话启动后闪现出主页。

图 2:登录屏幕

图 3:“新帐户”对话从登录页启动。

用代码实现对话框架使用

可以使用 ADF Faces 命令组件中的硬编码输出,但与任何“ action”一样,您还可以编程方式确定是否要显示普通“操作”方法中的对话(或要显示什么对话)。例如,某个操作可能检查用户是否使用错误的口令进行了登录,如果是,则显示一个错误对话,而不是继续正常导航。

    <af:commandButton id="cmdBtn" text="Login"
  
                                
action="#{backing_login.commandButton_action}"
useDialog="true"
windowHeight="200" windowWidth="500"/>

登录页中还包含一个登录 commandButton,它将用户转到页面流中的定单页。请参阅图 1。此 commandButton 调用后台 bean(称为 commandButton_action())的一个方法。 commandButton 也将 useDialog 设置为 true,以便操作方法中的输出返回一个包含“ process:”的字符串。还可以看到,我们在此处设置了实际对话的高度和宽度,而不是对话窗口中的页面高度和宽度。

  public String commandButton_action()
  {
String retValue;
    retValue = "orders";
_cust = getListCustomer();
    if (_cust == null || !password.equals(_cust.getPassword()))
    {
  
                              
retValue = "process:error";
} return retValue; }

以上的代码示例是由登录 commandButton 调用的 commandButton_action() 方法。此方法将缺省输出设置为“ orders”并获取尝试登录的客户。如果“数据库”中不存在用户,或输入的口令无效,则将输出重置为“ process:error”。此输出再加上我们在 Login 按钮上定义的 useDialog 属性将启动错误对话。参阅图 1:示例应用程序页面流。

所有这些技术都要求您使用“ process:”输出定义了导航规则。

<navigation-rule>
<from-view-id>/login.jspx</from-view-id>
<navigation-case>
  
                              
<from-outcome>process:newAccount</from-outcome>
<to-view-id>/new_account.jspx</to-view-id> </navigation-case> <navigation-case>
<from-outcome>process:error</from-outcome>
<to-view-id>/error.jspx</to-view-id> </navigation-case> <navigation-case>

在所有这些情况中,当您使用的是支持启动对话所需所有特性的 Web 浏览器时,将显示一个包含此对话的新窗口。但如果使用某个其他 Web 浏览器或不满足我们的需要的设备(如 PDA),您的对话框代码将仍然起作用!ADF Faces 并不启动一个新窗口,而只是在自动保留当前页面的所有状态后在当前窗口中向您显示对话页面。

    

图 4:与图 3 中显示的页面相同,但这次显示在 Palm 6.0.1 模拟器中

处理返回值并将值传递给对话

当流程结束时(有关它的含义的更多信息),命令组件将获取一个传递到 ReturnListener 的 ReturnEvent (如果您使用 addReturnListener 或使用“ returnListener”属性注册了一个 ReturnListener )。此对话可以向您返回一个值,如果它返回了值,它将作为 ReturnEvent 的属性自动传递给您。

                  <af:commandLink id="cmdLink" text="New user?"
action="process:newAccount" useDialog="true"
  
                                
returnListener="#{backing_login.handleReturn}"
partialSubmit="true" windowHeight="200" windowWidth="500"/>

在此示例中,我们将 commandLink 组件上的 returnListener 属性设置为指向称为 handleReturn () 的方法。

   public void handleReturn(ReturnEvent event)
   {
  
                              
if (event.getReturnValue() != null)
{
       Customer cst;     
String name;
String psw; 
                               
cst = (Customer)event.getReturnValue();
CustomerList.getCustomers().add(cst); name = cst.getFirstName(); psw = cst.getPassword();
                               
inputText1.setSubmittedValue(null);
inputText1.setValue(name); inputText2.setSubmittedValue(null); inputText2.setValue(psw);
     }
   }

当对话完成时将调用此方法。如果返回值为空,则表示用户取消了对话,因此我们不必执行任何操作。否则,此方法将相应地处理返回的对象和流程,在本例中这意味着向现有用户列表中添加新用户,并使用新添加的信息填充用户名和口令的输入域,以方便最终用户。只调用输入组件上的 setValue() 是不够的。还必须调用包含空值的 setSubmittedValue() 以清除最初提交的任何值。

如何完成流程

可以像编写任何其他 JSF 页面那样编写流程本身。唯一的差别是需要调用特殊的方法( AdfFacesContext.returnFromProcess() )以通知 ADF Faces 流程已完成。不管对话中是否显示流程,都可以调用此方法;如果它是对话,则对话窗口将自动关闭。您也不必在显示的第一个页面中调用它 - 可以根据需要从流程中的第一个页面导航到许多页面,只需在最后时调用 returnFromProcess() 。此方法还允许您从流程发送回“返回值”。在本示例中,“新帐户”对话实际上是两个页面 - new_account.jspx 和 account_details.jspx。account_details.jspx 页面是对话流中的最后一页,并且是将要调用 returnFromProcess() 方法的页面。此页面包含两个按钮 - Cancel 和 Done。当最终用户按 cancel 按钮时,我们只需关闭对话窗口并返回到初始页面。

现在,让我们看一看使用该流程的 account_details.jspx 页面:

<?xml version = '1.0' encoding = 'windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/EA15"
xmlns:afh="http://xmlns.oracle.com/adf/faces/EA15/html">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<afh:html>
<afh:head title="account_details">
<meta http-equiv="Content-Type"
content="text/html; charset=windows-1252"/>
</afh:head>
<afh:body>
<af:form>
<af:panelPage title="Account Details">
<af:messages/>
<afh:tableLayout>
...
              <afh:rowLayout>
<afh:cellFormat>
  
                              
<af:commandButton text="Cancel"
actionListener="#{backing_new_account.cancel}"/>
<af:commandButton text="Done"
actionListener="#{backing_new_account.done}"/>
</afh:cellFormat> </afh:rowLayout> </afh:tableLayout> </af:panelPage> </af:form> </afh:body> </afh:html> </f:view> </jsp:root>

每个按钮有两个方法 - cancel() 和 done()。以下代码演示了如何调用 returnFromProcess()。对于 cancel(),我们只返回空;对于 done(),我们首先检查用户是否为新帐户输入了有效口令,然后使用 returnFromProcess() 方法返回用户输入。但请注意,无论在哪个操作中我们都不知道该值将返回到何处 - 我们只是依靠 ADF Faces 框架指定将该值返回到何处。

  public void cancel(ActionEvent e)
  {
  
                              
AdfFacesContext.getCurrentInstance().returnFromProcess(null);
}
  public void done(ActionEvent e) 
  {
                               
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
...  
    if (!password.equals(confirmPassword))
    {
FacesMessage fm = new FacesMessage();
fm.setSummary("Confirm Password");
fm.setDetail("You have entered an incorrect password.Please verify that you entered correct password!");
FacesContext.getCurrentInstance().addMessage(null, fm); 
    }
else
    {
Customer cst = new Customer();
cst.setFirstName(firstname);
...
cst.setPassword(password);
                               
afContext.getCurrentInstance().returnFromProcess(cst);
afContext.getProcessScope().clear(); } }

如何与流程之间进行信息传递

您已经了解了如何通过将值传递给 AdfFacesContext 的 returnFromProcess() 方法来返回流程的值。但如何传入值?ADF Faces 使您可以使用“流程参数”的映射来提供流程(当然还包括对话)。(如果需要了解 processScope 工作方式的详细信息,请阅读 ADF Faces 开发人员指南中的 页面之间的通信:processScope 一章。)

当 ADF Faces 依据操作方法的输出而要启动流程或对话时,它将查询 LaunchEvent。此事件的方法之一是 getProcessParamters () ,它将为您提供可以向其中添加参数的映射。在示例应用程序中,我们使用此方法把在用户名域中输入的用户名传递到对话页面。

    <af:commandLink id="cmdLink" text="New user?"
                    action="process:newAccount" useDialog="true"
  
                              
launchListener="#{backing_login.handleLaunch}"
returnListener="#{backing_login.handleReturn}" partialSubmit="true" windowHeight="200" ++windowWidth="500"/>
   ...
   public void handleLaunch(LaunchEvent event)
        {
        //Pass the current value of the field into the dialog.
        Object usr = username;
        event.getProcessParameters().put("firstname", usr);
        }

采用此方法添加参数时,它们显示在 ADF Faces“ processScope”上的流程中,且流程中的页面可以获取 processScope 之外的值:

              <af:inputText label="First name"
value="#{processScope.firstname}"/>

ADF Faces processScope 与流程(和对话)之间有一个非常特定的交互。流程始终获取位于启动页面的 processScope 中的所有值的副本,以及位于 LaunchEvent 中的所有“流程参数”。但您对流程内部的 processScope 执行的任何操作都在此处结束 - 当您从流程返回时,processScope 将返回到它在流程开始前所处的位置。如果需要传递流程之外的值,则必须使用 AdfFacesContext.returnFromProcess() 、会话范围或应用程序范围。此方法意味着流程独立于调用代码,并为流程提供一个存储值的位置而不必担心覆盖上一级流程所需的内容。

示例应用程序中未使用但却很有用的内容

一个模式非常通用,因此我们为它提供了它自己的组件: < af:selectInputText > .这是一个可以启动对话框并自动接受它的返回值的输入组件,因此您不必像前一个示例那样组合 < af:inputText > 和 < af:commandButton >,您只需要:

         <af:selectInputText label="Pick a number:"value="(Empty)"
action="process:chooseInteger"
windowWidth="300" windowHeight="200"/>

此组件将自动处理对话的启动,接收 ReturnEvent 并更新域的值,一切都不需要您编写任何代码。(我们不需要“ partialSubmit”或“ useDialog”;它们由 < af:selectInputText > 自动设置。)

更多信息

有关 JSF 和 Oracle JDeveloper 10g (10.1.3) 开发人员预览版的更多信息,请访问位于 Oracle 技术网中的 Oracle JDeveloper 主页

Left Curve
热门下载
Right Curve