12
返回列表 发新帖
楼主: Sky-Tiger

怀疑论者的 JSF: JSF 应用程序的生命周期

[复制链接]
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
11#
 楼主| 发表于 2013-7-10 19:35 | 只看该作者
对域进行有效性验证
在 addCD方法被调用之前,JSF 必须对 GUI 中的域进行有效性验证。这实际上非常简单,因为您还没有为域关联任何有效性验证条件。在应用请求值阶段,这些值被从请求参数拷贝到组件值中(这是由组件本身进行的)。现在,价格从一个字符串转换为一个浮点类型。如果用户为价格输入的是“abc”,那么转换为浮点类型的操作就会失败,控制权将被重新定向到 cdForm.jsp 页面上,供最终用户进行修正。与价格相关的 h:message将显示一个转换错误消息。如果所有的值都可以正常进行类型转换,并且现在都可以使用了(如果需要的话),那么您就可以进行有效性验证的处理了。由于这个示例程序并没有与组件关联任何有效性验证规则(在下一篇文章中我们将介绍这种特性),因此您可以继续进入更新模型值的阶段了。
在更新模型值的阶段中,会使用保存在 GUI 组件中的经过转换和有效性验证的值来调用 CD 的赋值方法。addCD()方法是在 调用程序阶段中被调用的。addCD()方法使用一个业务代理(store对象)来执行这个操作。addCD方法在系统中使用 store对象来存储 CD。由于 addCD方法会返回成功,因此接下来会显示这个清单,这是在 faces-config.xm 中定义的。在 faces-config.xml 中定义的导航规则如清单 7 所示。

清单 7. addCD成功输出的导航规则
<navigation-rule>
  <from-view-id>/cdForm.jsp</from-view-id>
  <navigation-case>
    <from-action>#{CDManagerBean.addCD}</from-action>
    <from-outcome>success</from-outcome>
    <to-view-id>/listing.jsp</to-view-id>
  </navigation-case>
  ...
</navigation-rule>

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
12#
 楼主| 发表于 2013-7-11 19:57 | 只看该作者
使用案例 2:编辑 CD
这个示例程序的第二个使用案例也会在这个清单页面(listing.jsp)中启动。除了向您介绍如何编辑 JSF 页面中的数据之外,这个使用案例还将向您介绍 JSF dataTable组件。
这个清单页面使用一个 dataTable组件来显示 CD 的清单。dataTable的值被绑定到控制程序类 StoreController的 cds属性。cds属性的定义如清单 8 所示。

清单 8. 在 StoreController.java 中定义的 cds属性
[StoreController.java]
/** List of cds for CD listing. */
private DataModel cdModel = new ListDataModel();
{
    cdModel.setWrappedData(store.findTitleAsc());
}
/**
* List of CDs in the system.
*
* @return Returns the cds.
*/
public DataModel getCds() {
    return cdModel;
}

cds属性是基于从存储对象 StoreManagerDelegate返回的 java.util.List的,这个对象是该程序的业务代理。cdModel对从 DataModel中的存储对象返回的清单进行了封装。DataModel是一个用于 dataTable的模型。
dataTable的定义如清单 9 所示。

清单 9. listing.jsp 中的 dataTable定义
<f:view>
    <h:form>        
      <h:dataTable id="items"
        value="#{CDManagerBean.cds}"
        var="cd"
        rowClasses="oddRow, evenRow"
        headerClass="tableHeader">

注意该值被绑定到控制程序的 cds属性上。rowClasses和 headerClass属性用来指定 CSS 类,后者用来定义 dataTable的外观。正如前面介绍的一样,JSF 严重依赖于 CSS 来定义 GUI 的外观。如果您并不了解 CSS(即您之前都是使用字体标签和 HTML 表来设置外观的),就可能会希望在灵活运行 JSF 之前首先来学习一下有关 CSS 的知识。

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
13#
 楼主| 发表于 2013-7-11 19:57 | 只看该作者
column 组件
Title、Artist和 Price域都是使用 column组件显示的,如清单 10 所示(此处只显示了 Title域)。

清单 10. 在 column 组件中添加域
<h:column>
    <f:facet name="header">
      ...
        <hutputText value="Title"/>
    </f:facet>
      <h:commandLink action="#{CDManagerBean.editCD}">
        <hutputText value="#{cd.title}"/>
      </h:commandLink>
</h:column>

column组件是 dataTable的一个子组件。column组件使用一个子组件和一个 facet。facet是一个有名的子组件;它并不是一个子孙组件。column组件有一个名为 header的 facet,它定义了在 header 中显示的内容。对于本例来说,commandLink是 column组件的一个子孙组件。commandLink在一个链接中显示了 CD 的标题,该链接被绑定到操作 #{CDManagerBean.editCD}上。这个操作属性将 commandLink绑定到控制程序类的 editCD()方法上,如清单 11 所示。

清单 11. editCDcommandLink 的后台 bean 方法
[StoreController.java]
/**
* Edit the CD. This get executed before the edit cdForm
* page gets loaded.
*
* @return outcome
*/
public String editCD() {
    this.cd = (CD) cdModel.getRowData();
    this.cd = (CD) store.getCDById(cd.getId());

    if ((cd.getCategory() != null) || !"".equals(cd.getCategory())) {
        this.subCategoryList.setRendered(true);

        this.subCategories = getSubcategoriesList(cd.getCategory());
    } else {
        this.subCategoryList.setRendered(false);
    }

    this.editMode = true;

    return "success";
}

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
14#
 楼主| 发表于 2013-7-11 19:57 | 只看该作者
editCD()方法
editCD()方法是在 JSF 生命周期的调用程序阶段调用的。editCD()方法准备控制程序以使用编辑模式来显示 cdForm.jsp 页面。这是通过查看当前选定的 CD 来实现的,CD 是通过调用 cdModel.getRowData()方法来选择的。
注意 JSF DataModel允许您从比传统的 Web 应用程序更高的层次上使用数据。您并不需要对请求参数进行检查:只需要调用 cdModel.getRowData()方法向 DataModel(cdModel)查询已经选择了哪个 CD。这个更高级别的抽象对 Web 开发进行了相当程度的简化。
一旦取得当前选择的 CD 之后,就可以使用业务代理来加载该 CD 的最新拷贝了(store.getCDById())。在加载这个 CD 之后,store.getCDById()会激活 subCategory清单(假设这个 CD 已经关联了一个子目录),然后将 editMode属性设置为 true。回想一下,editMode属性是由 cdForm用来显示 Add 或 Update 按钮。最后,store.getCDById()方法返回 success。在清单 12 中重要的导航规则可以保证返回成功之后,切换到 cdForm.jsp 页面,如下所示。

清单 12. 一条重要的导航规则
<navigation-rule>
  <from-view-id>/listing.jsp</from-view-id>
  <navigation-case>
    <from-action>#{CDManagerBean.editCD}</from-action>
    <from-outcome>success</from-outcome>
    <to-view-id>/cdForm.jsp</to-view-id>
  </navigation-case>
  <navigation-case>
    <from-action>#{CDManagerBean.addNew}</from-action>
    <from-outcome>success</from-outcome>
    <to-view-id>/cdForm.jsp</to-view-id>
  </navigation-case>
</navigation-rule>

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
15#
 楼主| 发表于 2013-7-11 19:58 | 只看该作者
updateCD()方法
CD 表单会加载并显示 CD 属性的属性设置。最终用户可以根据需要编辑所得到的表单,并在完成时点击 Update 按钮。Update 按钮是当用户处于 Edit 模式时所显示的惟一一个按钮,它只会在 editMode为 true时显示,如清单 13 所示。

清单 13. Update CD 按钮
[cdForm.jsp]

<h:commandButton id="submitUpdate"
  action="#{CDManagerBean.updateCD}"
  value="Update CD"
  endered="#{CDManagerBean.editMode}"/>

Update 按钮被绑定到 updateCD()方法上。在调用 update 方法之前,JSF 必须对 GUI 中的域进行有效性验证。在应用请求值阶段,这些值被从请求参数中拷贝到组件值中(这是由组件本身完成的)。现在,价格被从一个字符串转换成了一个浮点类型。由于没有为组件关联任何有效性验证规则,因此如果所有请求的值都已经存在并经过转换了,就可以转换到生命周期的下一个步骤了。
更新模型值
在更新模型值阶段中,会使用保存在 GUI 组件中经过类型转换和有效性验证的值来调用 CD 的赋值函数。updateCD()方法是在调用程序阶段被调用的。updateCD()方法如清单 14 所示。

清单 14. updateCD()方法
[StoreController.java]

/**
* Update the CD loaded on the form.
*
* @return success
*/
public String updateCD() {
    store.updateCD(this.cd);
    this.editMode = false;

    return "success";
}

updateCD()方法可以代理业务代理的大部分职责。它将 editMode 设置为 false(这是默认值),并返回成功。成功输出将您重定向回清单页面中,在这个页面中您可以查看根据清单 15 中显示的导航规则新编辑的 CD。

清单 15. 成功的 UpdateCD 会将您带回 listing.jsp
[faces-config.xml]
<navigation-rule>
  <from-view-id>/cdForm.jsp</from-view-id>
  ...   
  <navigation-case>
    <from-action>#{CDManagerBean.updateCD}</from-action>
    <from-outcome>success</from-outcome>
    <to-view-id>/listing.jsp</to-view-id>
  </navigation-case>
</navigation-rule>

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
16#
 楼主| 发表于 2013-7-11 19:58 | 只看该作者
使用案例 3:对 CD 进行排序
我们要介绍的最后一个使用案例将向您展示如何对表进行排序。这个使用案例也是在 CD 清单页面上启动的。清单页允许根据标题和艺术家对 CD 按照升序或降序的顺序进行排列。在本例中,我将向您展示如何根据标题进行排序,并将根据艺术家进行排序留作练习。
标题头排序有一些到控制程序中排序方法的链接。清单 16 显示了在 listing.jsp 中是如何显示标题头的。

清单 16. 对 commandLinks 进行排序
[listing.jsp]
<h:column>
  <f:facet name="header">
    <hanelGroup>
      <hutputText value="Title"/>
      <f:verbatim>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[</f:verbatim>
      <h:commandLink styleClass="smallLink" action="#{CDManagerBean.sortTitleAsc}">
        <hutputText id="ascTitle" value="asc"/>
      </h:commandLink>
      <hutputText value=","/>
      <h:commandLink styleClass="smallLink" action="#{CDManagerBean.sortTitleDec}">
        <hutputText id="decTitle" value="dec"/>
      </h:commandLink>
      <f:verbatim>]</f:verbatim>
    </hanelGroup>
  </f:facet>
  <h:commandLink action="#{CDManagerBean.editCD}">
    <hutputText value="#{cd.title}"/>
  </h:commandLink>
</h:column>

panelGroup 组件
注意一下清单 16,链接是在标题列的 header facet 中定义的。facet 只会关联一个惟一名字的组件;这样,要在 header facet 中放置一个多链接的组件,您需要使用 panelGroup。panelGroup(与 panelGrid类似)是一个单独的组件,其中包含了很多子组件。panelGroup包含两个链接,如清单 17 所示。

清单 17. panelGroup组件链接
[listing.jsp]
<h:commandLink styleClass="smallLink" action="#{CDManagerBean.sortTitleAsc}">
    <hutputText id="ascTitle" value="asc"/>
</h:commandLink>
...        
<h:commandLink styleClass="smallLink" action="#{CDManagerBean.sortTitleDec}">
    <hutputText id="decTitle" value="dec"/>
</h:commandLink>

第一个链接被绑定到控制程序的 sortTitleAsc方法上,第二个链接被绑定到 sortTitleDec上。这两个方法如清单 18 所示。

清单 18. panelGroup链接方法
[StoreController.java ]
/**
* Uses the store delegate to return
* a sorted list of CDs by title (ascending).
*
* @return asc
*/
public String sortTitleAsc() {
    this.cdModel.setWrappedData(store.findTitleAsc());

    return "asc";
}

/**
* Uses the store delegate to return
* a sorted list of CDs by title (descending).
*
* @return dec
*/
public String sortTitleDec() {
    this.cdModel.setWrappedData(store.findTitleDec());

    return "dec";
}

这两个方法都依赖于业务代理返回一个按照正确要求排序后的 java.util.List。注意这个方法会返回逻辑输出 asc和 dec。这两个输出在 faces-config.xml 文件中都没有映射。没有映射的输出会导致重新加载当前的视图;这样,listing.jsp 将会在调用这些方法时重新进行加载,清单页面也会按照正确的顺序重新显示。
这种方法的优点是它依赖于业务代理进行排序。业务代理又可能会依赖于一个 DAO 对象,而后者又依赖于一个数据库查询或 OR 映射查询,这样可以对 CD 进行有效的查询。这种方法通常比具有一个“智能” GUI 组件的方法更好,后一种方法知道如何对随机的域对象(CD 就是一个域对象)进行排序,因为排序操作是一个经常发生的操作,严格来说,是模型的一部分(即域对象的一部分),而不是视图的一部分。
正如前面介绍的一样,对标题进行排序和对艺术家进行排序的代码几乎是相同的。作为一个练习,请自己试图为第四个使用案例编写代码,对艺术家而不是标题进行排序。

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
17#
 楼主| 发表于 2013-7-11 19:58 | 只看该作者
即时事件处理
我们要介绍的最后一个主题是即时事件处理。即时事件处理在您不希望(或需要)对整个页面进行有效性验证来处理用户输入的情况中非常有用。回想一下,示例程序的 cdForm.jsp 页面使用单选按钮来显示一个目录和子目录清单。当最终用户选择一个目录时,cdForm.jsp 页面就会使用 JavaScript 重新生成表单,这样就可以显示子目录清单了。
这是一个即时事件处理的例子,因为整个表单 没有在调用事件处理程序之前进行有效性验证。相反,类清单的事件处理程序会生成子目录,并强制 JSF 跳过进行响应的阶段。组件的事件处理程序通常都是在调用程序阶段执行的。即时事件组件的事件处理程序是在应用请求值阶段执行的,这发生在其余组件的类型转换和有效性验证之前。
清单 19 显示了在 cdForm.jsp 页面中再次显示的目录清单。

清单 19. cdForm.jsp 中的目录清单
[cdForm.jsp]
<h:selectOneRadio id="category" value="#{CDManagerBean.cd.category}"
  immediate="true"
  onclick="submit()"
  valueChangeListener="#{CDManagerBean.categorySelected}">
    <f:selectItems value="#{CDManagerBean.categories}"/>
</h:selectOneRadio>

selectOneRadio目录域被绑定到 CD的目录属性(value="#{CDManagerBean.cd.category}")上。注意这个即时事件处理被激活了(immediate="true")。这种设置意味着 Category组件的事件会在应用值阶段(而不是在调用程序阶段)进行处理(以及类型转换和有效性验证)。
JavaScript 功能是在 onclick="submit()"这一行 —— 即当用户进行修改时,它应该立即被提交到 Web 程序中进行处理。
事件处理程序方法
在清单中显示的可用分类是由 f:selectItems标签值(value="#{CDManagerBean.categories}")确定的。这个组件的事件处理程序的变化是控制程序的 categorySelected()方法(valueChangeListener="#{CDManagerBean.categorySelected}")。事件处理程序如清单 20 所示。

清单 20. categorySelected事件处理程序
[StoreController.java]
/**
* Event Handler for a category getting selected.
*
* @param event event data
*/
public void categorySelected(ValueChangeEvent event) {
    subCategoryList.setRendered(true);

    String value = (String) event.getNewValue();

    if (value != null) {
        this.subCategories = this.getSubcategoriesList(value);
    }

    FacesContext context = FacesContext.getCurrentInstance();
    context.renderResponse();
}

categorySelected()方法做的第一件事情是允许 subCategoryList调用自己。categorySelected()方法然后会使用所选择的分类值来查找一个 subCategories清单。subCategories属性被绑定到 subcategoryList值上。接下来,事件处理程序通过调用当前 FacesContext上的 renderResponse()方法强制 JSF 转到进行响应阶段。然后,GUI(cdForm.jsp)为当前显示的目录重新显示可用的子目录。
将组件绑定到控制程序上
subCategoryList组件是从 GUI 上绑定的。正如您可以将值绑定到组件上一样,您也可以将这些组件绑定到一个控制程序上。子目录是在 cdForm.jsp 页面中定义的,如清单 21 所示。

清单 21. 在 cdForm.jsp 页面中定义的子目录清单
[cdForm.jsp]
<h:selectOneListbox id="subcategory" value="#{CDManagerBean.cd.subCategory}"
  binding="#{CDManagerBean.subCategoryList}">
    <f:selectItems value="#{CDManagerBean.subCategories}"/>
</h:selectOneListbox>

binding属性允许您将 GUI 的组件绑定到后端的 bean(控制程序)上。这样,上面的组件就会被绑定到 CDManagerBean.subCategoryList上,这是在清单 22 中定义的控制程序中的一个属性。

清单 22. subCategoryList属性
[StoreController.java ]
/** GUI Component that represents
   the Subcategory list on the CDForm. */
private UIInput subCategoryList;

{
    subCategoryList = new HtmlSelectOneListbox();
}

/**
* Subcategory list component
*
* @param aSubCategoryList The subCategoryList to set.
*
* @uml.property name="subCategoryList"
*/
public void setSubCategoryList(UIInput aSubCategoryList) {
    this.subCategoryList = aSubCategoryList;
}

/**
* Subcategory list component
*
* @return Returns the subCategoryList.
*
* @uml.property name="subCategoryList"
*/
public UIInput getSubCategoryList() {
    return subCategoryList;
}

即时事件处理只使用了很少的一点 JavaScript 功能(onclick="submit()"命令),这种灵活的 JSF 生命周期的便利可以让您处理在一个组件中输入的信息,而不用对整个页面进行有效性验证。在这个例子中,我们已经介绍了 Category组件的即时处理是如何让您可以显示子目录,而不用重新加载页面的。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表