java创建 jsf项目
JSF是一种非常稳定的流行Web框架,已在Java™平台企业版(Java EE)域中得到广泛使用。 Dojo是功能最强大的Web 2.0库之一,使您能够为Web应用程序创建丰富的界面。
通过同时利用这两种技术,您可以获得两种技术的优势。 在服务器端,好处包括组件的端到端生命周期管理,后端bean数据绑定和事件处理。 在客户端,您可以利用Dojo的丰富窗口小部件,实时动画(例如淡入淡出和滑动)以及拖放功能。 此外,借助Dojo框架的API支持,您可以通过在客户端添加更多逻辑来提高性能。
本文介绍了以下利用JSF和Dojo技术的方法:
- 在客户端延迟绑定JSF组件和Dojo小部件
- 构建定制的JSF组件以启用Dojo小部件
- 延迟注入和将JSF组件解析为Dojo小部件
本文中的示例JSF应用程序由两页组成:“创建项目”和“项目结果”。 图1和图2显示了这两页。 为了简单易懂,本文中描述的所有方法都使用了这些图中显示的样本,以演示如何将Dojo与JSF应用程序结合起来。 (您也可以查看图1的大图。)
图1.创建项目
图2.项目结果
该场景确实非常简单。 用户可以输入一些信息来创建项目,然后单击提交按钮,将显示项目信息。 因此,您的任务是将输入框,文本区域和其他JSF组件转换为Dojo小部件。
方法1:在客户端延迟绑定JSF组件和Dojo小部件
这种方法是将Dojo样式应用于JSF组件的最简单方法。 我们只使用JavaScript在客户端将JSF组件和Dojo小部件绑定在一起。 通过使用JavaScript,可以在JSF组件和Dojo小部件之间传输数据并使它们保持一致。
首先,看一下原始的JSF页面。 清单1显示了JSF代码。 (清单1中的代码已格式化为适合本文的内容。单击此处查看完整的代码清单。)
清单1.原始的Create Project JSF页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://www.ibm.com/jsf/html_extended" prefix="hx"%>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<body>
<f:view>
<h2>Make your JSF application Dojoable</h2>
<h3>Create Project</h3>
<h:form id="project">
<table>
<tbody>
<tr>
<td>Project Name:</td>
<td><h:inputText id="projectName"
value="#{projectFormBean.projectName}" size="5"/></td>
</tr>
<tr>
<td>Project description:</td>
<td><h:inputTextarea id="projectDescription"
value="#{projectFormBean.projectDescription}" rows="2"
cols="15"/></td>
</tr>
<tr>
<td colspan=2>
<h:commandButton id="button_submit" action="success"
value="Submit" type="submit"></h:commandButton>
</td>
</tr>
</tbody>
</table>
</h:form>
</f:view>
</body>
有两个输入字段:项目名称和项目描述。 这两个字段都与后端表单bean projectFormBean
。
让我们开始以Dojo样式转换此页面。
步骤1.隐藏JSF组件并添加Dojo小部件
您需要隐藏原始的JSF组件并将其更改为不可见,然后在此页面中添加相关的Dojo小部件。 第一步是导入Dojo库,并声明需要哪些小部件,如清单2所示。(清单2中的代码已经格式化为适合本文的内容。有关完整的代码,请单击此处 。)
清单2.导入Dojo库并声明小部件
<style type="text/css">
@import url("${pageContext.request.contextPath}/script/
dojo_lib/dijit/themes/tundra/tundra.css");
@import url("${pageContext.request.contextPath}/script/dojo_lib/
dojo/resources/dojo.css");
</style>
<script type="text/javascript" src="${pageContext.request.contextPath}/script/
dojo_lib/dojo/dojo.js"
djConfig="parseOnLoad: true, isDebug:false"></script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Textarea");
</script>
在此示例中,我们使用tundra主题作为默认样式,因此必须首先导入tundra.css和dojo.css。 接下来,您需要导入dojo.js文件,该文件是Dojo的主要js脚本文件。 最后,您必须声明要使用的窗口小部件。 在这种情况下,我们将使用validationTextBox
和Textarea
。 Dojo使用按需机制。 它只会根据声明加载所需的文件。 因此,它可以提高应用程序的性能并减少网络传输。
下一步是将JSF组件更改为不可见,并添加Dojo小部件以进行显示。 修改后的jsp如清单3所示。
清单3.将JSF组件更改为不可见,并添加Dojo小部件
<tr>
<td>Project name:</td>
<td><h:inputHidden value="#{projectFormBean.projectName}"/><f:verbatim>
<input type="text" name="projectName"
dojoType="dijit.form.ValidationTextBox"
regExp="[\w]+"
required="true"
invalidMessage="Invalid project name."/></f:verbatim>
</td>
</tr>
<tr>
<td>Project description: </td>
<td><h:inputHidden value="#{projectFormBean.projectDescription}"/><f:verbatim>
<textarea dojoType="dijit.form.Textarea" style="width:80%">
</textarea></f:verbatim>
</td>
</tr>
我们将原始JSF组件更改为inputHidden
,但仍与后端projectFormBean
数据绑定。 在这个隐藏的组件之后,我们添加了相应的Dojo小部件。 对于“项目名称”字段,我们使用validationTextBox
来验证客户端的输入框,对于“项目描述”字段,我们使用dojo textarea
,它可以自动扩展而无需滚动条。
步骤2.在页面渲染阶段将数据从JSF组件复制到Dojo小部件
之前,我们用页面中的Dojo小部件替换了JSF组件,但是页面中的数据未显示在Dojo小部件中。 接下来,我们将向您展示如何将数据从JSF组件复制到Dojo小部件。
在渲染阶段,我们使用JavaScript从JSF组件中提取数据并将其设置为相关的Dojo小部件。 清单4显示了JavaScript代码。
清单4.在渲染阶段将数据从JSF组件复制到Dojo小部件
<script type="text/javascript">
function dojoInit(){
dijit.registry.byClass('dijit.form.ValidationTextBox').forEach(function(pane){
pane.setValue(pane.domNode.previousSibling.value);
});
dijit.registry.byClass('dijit.form.Textarea').forEach(function(pane){
pane.setValue(pane.domNode.previousSibling.value);
});
}
dojo.addOnLoad(dojoInit);
</script>
在dojoInit
函数中,您将找到由类名查询的所有Dojo小部件。 对于每个窗口小部件,您都可以在Dom树(它是JSF组件)中找到其先前的同级节点。 然后,您从JSF组件中提取数据并将其设置为查询的Dojo小部件。 这样,您可以在JSF组件和Dojo小部件之间同步数据。 最后,您使用dojo.addOnLoad
函数来确保在渲染阶段调用了dojoInit
函数。
注意 :使用Dojo工具包时,应避免使用<body onload="...">
或window.onload
因为Dojo具有使用window.onload
自己的初始化机制。 您自己使用此机制将干扰Dojo的初始化例程。 Dojo提供了dojo.addOnLoad
,它使您可以在Dojo完成自己的初始化之后加载函数。
步骤3.在页面提交阶段将数据从Dojo小部件复制到JSF组件
当前,可以在页面中以正确的数据显示dojo小部件。 但是,如果用户更改了数据,这些更改如何反映到相应的后端Form bean? 在这一步中,我们展示了在页面提交阶段如何在Dojo小部件和JSF组件之间同步数据。
首先,您必须创建一个JavaScript函数以将数据从Dojo小部件复制到JSF组件。 代码如清单5所示。
清单5.将数据从Dojo小部件复制到JSF组件
function setDojoValue(){
dijit.registry.byClass('dijit.form.ValidationTextBox').forEach(function(pane){
pane.domNode.previousSibling.value = pane.getValue();
});
dijit.registry.byClass('dijit.form.Textarea').forEach(function(pane){
pane.domNode.previousSibling.value = pane.getValue();
});
}
此函数类似于第2步中显示的dojoInit
函数。我们仍然使用dojo查询方法通过指定的类名查找所有dojo小部件。 然后,我们从这些Dojo小部件中提取数据并将其设置为相应的JSF组件。
接下来,我们必须在页面提交时调用setDojoValue
函数,因此我们将更改原始的setDojoValue
按钮,如清单6所示。
清单6.带有JavaScript函数的Submit按钮
<h:commandButton id="button_submit" action="success"
value="Submit" type="submit" onclick="setDojoValue();"></h:commandButton>
我们在JSF commandButton
添加onclick
属性,因此当用户单击此按钮时,将首先调用JavaScript函数,然后提交页面。 这样,我们可以在提交页面之前将Dojo小部件中的所有数据复制到JSF组件,并且数据更改将反映到后端Form Bean。
这就是使用此方法所需要做的。 图3显示了新的Create Project页面。
图3.使用方法1创建项目
如您所见,我们使用一个经过验证的输入框来支持客户端验证。 我们还使用了dojo textarea
,它可以在没有滚动条的情况下自动扩展。
方法2:构建定制的JSF组件以启用Dojo小部件
JSF是功能强大的Web框架。 它不仅为Web应用程序提供了标准的用户界面组件,而且还是允许用户自定义的非常灵活的API。 在本节中,我们将说明如何结合Dojo风格开发自己的JSF组件。
通常,JSF组件将包含以下部分:
- UIComponent类:从
UIComponentBase
或其他现有JSF组件(例如outputText
派生的类。 此类表示整个JSF组件的核心逻辑。 - 渲染类:此类用于渲染组件。 通常,它将处理如何生成用于渲染HTML代码,因此对于本节至关重要。 我们需要更改此类,并使其生成Dojo样式HTML代码。
- UI组件标签类:这是一个JSP标签处理程序类,它使UI组件可以在JSP页面中使用。 它还可以将单独的呈现类与UIComponent类相关联。
- 标记库描述符文件:这是标准的JavaEE JSP标记库描述符(tld)文件,该文件将标记处理程序类与JSP页面中的可用标记相关联。
我们将继续以创建项目场景为例,并且将展示如何创建具有dojo验证功能的输入文本JSF组件。
首先,看一下定制的JSF标记。 清单7显示了如何在Web应用程序中使用定制的JSF标记:
清单7.使用定制的JSF标记
<h:form id="project">
<table>
<tbody>
<tr>
<td>Project Name:</td>
<td>
<jsfdojo:input id="projectName" type="text"
invalidMessage="Invalid project name."
dojoType="dijit.form.ValidationTextBox"
dojoRequired="true"
regExp="[\w]+"
value="#{projectFormBean.projectName}"/>
</td>
</tr>
图4显示了使用这种方法的网页。 (您还可以查看图4的大图。)
图4.创建项目-方法2
要开发具有Dojo风格的自己的JSF输入框,可以使用现有的JSF输入框作为指南,因此不需要实现自己的UIComponent类。 您可以重用现有的。 您确实需要实现render和tag类,但这并不是很困难。 您可以继承现有的JSF API,只需重写一些功能即可。
清单8显示了用于在JSP页面中处理标签的标签类的代码。 它还将UIComponent和Render类相关联。
清单8.标签类
public class InputTag extends InputTextTag {
@Override
public String getComponentType() {
return "javax.faces.HtmlInputText";
}
@Override
public String getRendererType() {
return "jsfdojo.input.render";
}
private String dojoType;
private String invalidMessage;
private String regExp;
private String type;
private String dojoRequired;
public String getDojoRequired() {
return dojoRequired;
}
public void setDojoRequired(String dojoRequired) {
this.dojoRequired = dojoRequired;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getRegExp() {
return regExp;
}
public void setRegExp(String regExp) {
this.regExp = regExp;
}
public String getDojoType() {
return dojoType;
}
public void setDojoType(String dojoType) {
this.dojoType = dojoType;
}
public String getInvalidMessage() {
return invalidMessage;
}
public void setInvalidMessage(String invalidMessage) {
this.invalidMessage = invalidMessage;
}
@Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
component.getAttributes().put("dojoType", dojoType);
component.getAttributes().put("dojoRequired", dojoRequired);
component.getAttributes().put("invalidMessage", invalidMessage);
component.getAttributes().put("regExp", regExp);
component.getAttributes().put("type", type);
}
}
标签类从InputTextTag
继承。 您需要重写某些功能,并为此标签中使用的新属性添加一些新字段。
getComponentType
函数指定UIComponent类,而getRendererType
函数指定Render类。 因此,如果JSF组件已经分离了render类,则在标记类中,这两个函数用于将它们链接在一起。
标记类中的字段表示JSP标记中使用的属性。 setProperties
函数用于将这些属性存储在此组件的属性映射中。 该地图将在render类中使用。
如上所述,我们将实现一个单独的渲染类。 此类负责生成HTML代码以在网页中呈现。 清单9显示了此render类的代码。
清单9.分离的渲染类
public class InputRender extends TextRenderer {
@Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("input", component);
String id = (String) component.getClientId(context);
writer.writeAttribute("id", id, "id");
writer.writeAttribute("name", id, "id");
writer.writeAttribute("value", getValue(component), "value");
writer.writeAttribute("type", (String)
component.getAttributes().get("type"), null);
writer.writeAttribute("dojoType", (String)
component.getAttributes().get("dojoType"), null);
writer.writeAttribute("required", (String)
component.getAttributes().get("dojoRequired"), null);
writer.writeAttribute("invalidMessage", (String)
component.getAttributes().get("invalidMessage"), null);
writer.writeAttribute("regExp", (String) component.getAttributes().get("regExp"), null);
writer.endElement("input");
}
protected Object getValue(UIComponent component) {
Object value = null;
if (component instanceof UIInput) {
value = ((UIInput) component).getSubmittedValue();
}
if (null == value && component instanceof ValueHolder) {
value = ((ValueHolder) component).getValue();
}
if (value == null) {
value = "";
}
return value;
}
}
标记类继承自TextRenderer
,因此您无需担心解码问题。 您所需要做的就是实现编码输出,该输出由encodeEnd
函数实现。 在此功能中, ResponseWrite
对象使生成HTML代码变得容易。 对于诸如type
, dojoType
, required
, invalidMessage
和regExp
type
的新添加属性,这些属性的值是从组件的属性映射中检索的,该属性映射在标记类中设置。
从getValue
函数中实现的UIComponent检索value
属性的value
。
在实现标记和渲染类之后,还需要为此JSF组件定义标记库描述符文件(tld文件)。 清单10显示了此tld文件的内容。
清单10.标签库描述符文件– tld文件
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>jsfdojo</short-name>
<uri>http://jsfdojo.ibm.com</uri>
<tag>
<name>input</name>
<tag-class>jsf.input.InputTag</tag-class>
<body-content>empty</body-content>
<description>
</description>
<attribute>
<name>dojoType</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
</description>
</attribute>
<attribute>
<name>dojoRequired</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
</description>
</attribute>
<attribute>
<name>type</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
</description>
</attribute>
<attribute>
<name>invalidMessage</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
</description>
</attribute>
<attribute>
<name>regExp</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
</description>
</attribute>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
Value reference expression pointing the attribute that
will have the credit card number.
</description>
</attribute>
……
</tag>
</taglib>
在此tld文件中,“标签类别”字段指定已实现的标签类别。 它还描述了将在此标记中使用的属性。 在这种情况下,我们添加了几个新属性: dojoType
, dojoRequired
, type
, invalidMessage
和regExp
。 还有许多超类属性,例如id
, value
等。
最后,您需要在faces-config.xml文件中注册新组件。 清单11显示了此组件的register部分。
清单11.在faces-config.xml中注册JSF组件
<render-kit>
<renderer>
<description>
Renderer customized input with dojo style
</description>
<component-family>javax.faces.Input</component-family>
<renderer-type>jsfdojo.input.render</renderer-type>
<renderer-class>
jsf.input.InputRender
</renderer-class>
</renderer>
</render-kit>
组件族和渲染器类型字段与标记类中的函数完全映射。 清单12显示了这些功能。
清单12.覆盖组件和渲染器字段
@Override
public String getComponentType() {
return "javax.faces.HtmlInputText";
}
@Override
public String getRendererType() {
return "jsfdojo.input.render";
}
这是实现自己的JSF组件所需要做的全部工作。 它涉及几个不同的类和xml实现,但是它非常简单且不复杂,尤其是在基于现有组件的情况下。 在这种情况下,我们只需添加一些新属性并覆盖编码功能即可生成Dojo HTML代码。
方法3:惰性注入(将JSF组件后解析到Dojo小部件中)
这种方法是使用JavaScript在客户端完全执行的,但是与本文中的第一种方法完全不同。 惰性注入意味着在整个页面加载之后将Dojo小部件信息注入到JSF组件中。 然后,使用Dojo解析器来解析JSF组件并将其转换为Dojo小部件。 在注入过程中,可能会注册一个处理程序,稍后将在提交JSF表单时调用该处理程序。 这样,可以将精确的数据传送回服务器端的相应JSF侦听器。 图5显示了此方法的详细工作流程。 (您还可以查看图5的大图。)
图5.惰性注入方法的工作流程
步骤1:将Dojo小部件信息注入JSF组件。
您可能已经注意到,在整个页面加载完成之后,JSF组件已转换为HTML标记。 例如,清单13是JSF inputText
组件的语法。 加载页面之后,将inputText
组件转换为HTML标记,如清单14所示。
清单13. JSF inputText组件的语法
<td>
<h:inputText id="projectName" value="#{projectFormBean.projectName}" size="5"/>
</td>
清单14.为JSF inputText组件生成HTML标记
<td>
<input type="text" name="project:projectName" id="project:projectName" size="5"/>
</td>
因此,这个想法很简单。 将必要的窗口小部件信息添加到生成HTML标记中,以便Dojo解析器可以识别并将其解析为Dojo窗口小部件。 另外,为了区分普通HTML标记和从JSF组件生成的标记,在JSF组件的包装器节点上标记了特殊属性jsf2dojo='true'
。 清单15显示了针对Create Project示例的更新后的JSF页面。 您可以在NewProject_lazy_inject.jsp文件中查看更多详细信息。
清单15.使用jsf2dojo ='true'属性更新的JSF页面
<tr>
<td jsf2dojo='true'>
<h:inputText id="projectName" value="#{projectFormBean.projectName}"
size="5"/>
</td>
</tr>
<tr>
<td jsf2dojo='true'>
<h:inputTextarea id="projectDescription"
value="#{projectFormBean.projectDescription}" rows="2" cols="15"/>
</td>
</tr>
这样,我们可以在页面中标记部分JSF组件,并将它们转换为Dojo小部件。 其他JSF组件将保持不变。 请注意,将jsf2dojo='true'
属性添加到包装器节点而不是JSF组件本身,因为否则,JSF编译器会将其视为带有编译错误的无效属性。
步骤2:将注入的JSF组件解析到Dojo小部件中
Dojo注入完成后,下一步是将JSF组件解析为Dojo小部件。 Dojo解析器是所有小部件最重要的基础。 默认情况下,页面加载后,Dojo解析器会扫描整个页面以查找带有Dojo标记的标签(例如dojoType='xxx'
)并将其解析为小部件。 Dojo解析器的另一种用法是动态地解析标签,或者,正如我们所说的, 延迟解析 。 这是我们的方法。
所有解析逻辑都封装在jsf2dojo.js文件中,因此第一步是将其包含在JSF页面中,如清单16所示。
清单16.导入jsf2dojo.js
<script type="text/javascript" src="script/jsf2dojo.js"></script>
接下来,将所有解析步骤添加到jsf2dojo.js。 第一步是小部件声明,如清单17所示。
清单17.窗口小部件类声明和页面onload处理程序
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Textarea");
dojo.require("dojo.parser");
var TYPE_MAP = {
text : 'dijit.form.ValidationTextBox',
textarea : 'dijit.form.Textarea'
};
dojo.addOnLoad(init);
小部件类是为“创建项目”示例声明的。 在这里,使用了Dojo ValidationTextBox
和Textarea
小部件。 还包括Dojo解析器。 TYPE_MAP
常量定义了我们稍后将使用的类映射。 dojo.addOnLoad(init)
函数注册一个回调函数,该函数将在整个页面加载完毕且Dojo完成初始化之后被调用。
清单18显示了函数init
的内容。
清单18.将JSF组件解析为Dojo小部件
function init(){
var jsfContainerList = dojo.query("[jsf2dojo='true']");
for(var i = 0 ; i < jsfContainerList.length; i++){
var jsfWidget = _getJsfWidget(jsfContainerList[i]);//get JSF component node
if(!jsfWidget) continue;
if('input' == jsfWidget.tagName.toLowerCase()
&& 'text' == jsfWidget.type.toLowerCase()){ // input
jsfWidget.setAttribute('dojoType',
TYPE_MAP[jsfWidget.type.toLowerCase()]);
jsfWidget.setAttribute('promptMessage',
"Please Enter your information");
jsfWidget.setAttribute('required',"true");
}else{
jsfWidget.setAttribute('dojoType',
TYPE_MAP[jsfWidget.tagName.toLowerCase()]);
}
dojo.parser.parse(jsfContainerList[i]);//parse wrapper node
}
}
init
函数包含所有解析逻辑。 首先使用dojo.query("[jsf2dojo='true']")
获取包含我们先前定义的JSF组件的所有包装器( <td>
)。 然后, _getJsfWidget()
用于获取从JSF组件生成的确切HTML标记。 对于JSF inputText
组件,我们添加了dojoType=' dijit.form.ValidationTextBox'
和其他特定属性,包括提示消息以及该属性是否为必需。 对于JSF inputTextarea
组件,我们添加了dojoType=' dijit.form.Textarea'
。 因为一切准备就绪, dojo.parser.parse()
调用dojo.parser.parse()
生成Dojo小部件。 这里,JSF组件的包装器节点(<td>)用于解析。
步骤3:验证页面
您可以通过访问http:// [服务器ip]:[服务器端口] /JSFSampleEarWeb/faces/NewProject_lazy_inject.jsp来验证结果。 您应该在页面上看到Dojo小部件,如图6所示。
图6.创建项目-方法3
单击提交之后,项目名称和描述字段将传输到服务器端的JSF侦听器,并显示在另一个名为Project_result.jsp的JSF页面上。
进阶主题
您可能已经注意到,上面列出的步骤仅处理JSF组件与Dojo小部件具有紧密映射的简单情况。 如果映射不是那么简单怎么办? 如前所述,需要一些额外的技巧。
首先,我们将向页面添加更多的JSF组件,如清单19所示。
清单19.添加了更多组件的JSF页面
<tr>
<td jsf2dojo='true'>
<h:selectOneMenu id="projectOption" value="location">
<f:selectItem id="select1" itemValue="Shanghai" itemLabel="Shanghai"/>
<f:selectItem id="select2" itemValue="London" itemLabel="London"/>
<f:selectItem id="select3" itemValue="New York" itemLabel="New York"/>
</h:selectOneMenu>
</td>
</tr>
<tr>
<td jsf2dojo='true' jsfType='radio'>
<h:selectOneRadio id="newPrjRadio" value="NYPrj">
<f:selectItem id="rd1" itemLabel="Yes" itemValue="y" />
<f:selectItem id="rd2" itemLabel="No" itemValue="n" />
</h:selectOneRadio>
</td>
</tr>
<tr>
<td jsf2dojo='true'>
<h:selectBooleanCheckbox id="ck1" value="manager" />Project Manager
<h:selectBooleanCheckbox id="ck2" value="technical leader"/>Techinal Leader
<h:selectBooleanCheckbox id="ck3" value="member" /> Developer&Tester
</td>
</tr>
<tr>
<td jsf2dojo='true'>
<h:commandButton id="btnSubmit" action="success" value="Submit"
type="submit"></h:commandButton>
</td>
<td><input id='btnSubmit_mock' type='text' style='display:none'/></td>
</tr>
在这里,为JSF selectOneRadio
组件添加了新属性jsfType='radio'
,并且还为JSF commandButton
组件添加了隐藏的输入文本。 这些属性稍后在解析过程中使用。 有关更新页面的更多详细信息,请参考NewProject_lazy_inject_advanced.jsp文件。
接下来,您将解析新的JSF组件。
在某些情况下,将JSF组件转换为Dojo小部件后,HTML源将变得完全不同。 例如,清单20显示了JSF selectOneMenu
组件HTML源,清单21显示了selectOneMenu
被转换为Dojo ComboBox
小部件之后的源。
清单20. JSF selectOneMenu组件HTML源
<select id="project:projectOption" size="1" name="project:projectOption">
<option value="Shanghai">Shanghai</option>
<option value="London">London</option>
<option value="New York">New York</option>
</select>
清单21. Dojo ComboBox小部件HTML源
<div id="widget_project:projectOption" class="dijit ..." value="Shanghai"
widgetid="project:projectOption" ...>
<div style="overflow: hidden;">
...
<div class="dijitReset dijitInputField">
<input id="project:projectOption" class="dijitReset" type="text"/>
</div>
</div>
</div>
为了使这些差异对服务器端的JSF侦听器透明,在提交JSF表单时将注册并调用一个处理程序。 清单22显示了将更多JSF组件解析为Dojo小部件的更新逻辑。
清单22.将更多的JSF组件解析为Dojo小部件
for(var n = 0; n < jsfWidgets.length; n++){
var jsfWidget = jsfWidgets[n];
if ('select' == jsfWidget.tagName.toLowerCase()){
jsfWidget.setAttribute(DOJO_TYPE,
TYPE_MAP[jsfWidget.tagName.toLowerCase()]);
mockFunc = _mockSelect;
}else if('textarea' == jsfWidget.tagName.toLowerCase()){
jsfWidget.setAttribute(DOJO_TYPE,
TYPE_MAP[jsfWidget.tagName.toLowerCase()]);
}else if('input' == jsfWidget.tagName.toLowerCase()){
var type = jsfWidget.type.toLowerCase();
if('submit' == type){
var params = {label: jsfWidget.value};
var dojoWidget = new dijit.form.Button(params, jsfWidget);
dojo.connect(dojoWidget, 'onClick', window, 'formSubmit');
mockFunc = _mockSubmitInput;
}else if('text' == type){
jsfWidget.setAttribute(DOJO_TYPE, TYPE_MAP[type]);
jsfWidget.setAttribute('promptMessage',
"Please Enter your information");
jsfWidget.setAttribute('required',"true");
}else if ('checkbox' == type || 'radio' == type){
jsfWidget.setAttribute(DOJO_TYPE, TYPE_MAP[type]);
}
}
if(!dojoWidget){
dojoWidget = dojo.parser.parse(jsfList[j])[0];//parse wrapper node
}
if(dojoWidget && mockFunc){
mockFuncMap.push({'dojoWidget': dojoWidget, 'mockFunc': mockFunc});
}
}
回调处理程序_mockSelect
和_ mockSubmitInput
已为JSF selectOneMenu和commandButton注册。 如清单23所示,这些处理程序将数据绑定到相应的字段,以确保表单信息在HTTPRequest
保持相同。 这样,服务器端的JSF侦听器不会意识到任何更改。 请注意,Dojo Button
是专门为commandButton
创建的。
清单23. JSF selectOneMenu的示例回调处理程序
Function _mockSelect(dojoWidget){
var mockedSelect = dojo.byId(dojoWidget.id);
mockedSelect.value = (dojoWidget.item) ? dojoWidget.item.value :
dojoWidget.store.root.value;
}
基本上,所有回调处理程序都有两个步骤:首先找到相应的字段,然后添加必要的值。 如清单22所示,通过使用dojo.connect(dojoWidget, 'onClick', window, 'formSubmit')
函数,在提交JSF表单时将调用formSubmit
。 清单24显示了formSubmit
的详细信息。
清单24.在提交JSF表单之前调用formSubmit函数。
function formSubmit(event){
for(var i = 0 ; i < mockFuncMap.length; i++){
mockFuncMap[i]['mockFunc']( mockFuncMap[i]['dojoWidget'] );
}
event.target.form.submit(); //event.target - submit button
}
formSubmit
唯一要做的就是调用所有已注册的回调处理程序,以便表单信息正确。 一切对服务器端的JSF侦听器都是透明的。
现在,通过访问http:// [服务器ip]:[服务器端口] /JSFSampleEarWeb/faces/NewProject_lazy_inject_advanced.jsp来验证结果。 您将看到所有JSF组件现在都已转换为Dojo小部件,如图7所示。
图7.创建带有更多小部件的项目-方法3
比较方式
在前面的部分中,我们列出了将JSF应用程序与Dojo结合起来的三种方法。 每种方法都有其优点和缺点。 本节从以下方面比较这三种方法:
- 复杂性-实施起来容易还是困难? 它需要修改许多文件还是需要修改配置文件?
- 适用性-是否适用于所有JSF组件? 有什么限制吗?
- 可重用性-是否易于重用? 是否有可能成为可以直接由其他项目使用的独立组件?
表1显示了这三种方法的比较。
表1.这三种方法的比较
方法 | 复杂 | 适用性 | 可重用性 |
---|---|---|---|
方法1 | 1个 | 2 | 2 |
方法2 | 3 | 3 | 3 |
方法3 | 2 | 3 | 2 |
注意 :我们只使用1到3之间的数字来表示各个方面的程度。 1表示最低级别,而3表示最高级别。
从该表中可以看出,方法1是最容易实现的方法,但有其局限性。 这种方法不适用于每个JSF组件,例如Checkbox
。 因此,方法1是将某些JSF组件( input box
, textarea
等)快速转换为Dojo小部件的好方法。
方法2稍微复杂一点,因为构建自己的JSF组件不是很简单。 但是这种方法适用于许多JSF组件,并且非常易于重用。 带有Dojo的JSF组件可以被其他项目直接使用。 因此,方法2是企业开发的不错选择,因为许多项目可能具有相同的要求。
方法3是中级。 它不像方法2那样复杂,并且适用于许多JSF组件,但是重用并不是很容易。 它需要有关Dojo小部件框架的更多知识。 因此,此方法适用于单个项目的开发。 如果只有一个项目需要Dojo的JSF组件,并且您不想花费很多时间来实现它,那么方法3是您的最佳选择。
结论
集成JSF和Dojo非常有用。 它利用JSF的服务器端功能和Dojo的强大小部件,使您可以轻松构建具有更好用户体验的Web应用程序。 本文提供了三种使用Dojo修改JSF应用程序的方法。 每种方法都有其优点和缺点,因此您可以选择最适合您的项目要求的方法。
翻译自: https://www.ibm.com/developerworks/web/library/wa-aj-jsfdojo/index.html
java创建 jsf项目