先上代码 TextBox.java package org.dojo4j.component.form;import javax.faces.component.FacesComponent;import javax.faces.component.html.HtmlInputText;@FacesComponent(TextBox.COMPONENT_TYPE)//直接继承JSF基础组件HtmlCommandButton,这
先上代码
TextBox.java
package org.dojo4j.component.form;
import javax.faces.component.FacesComponent;
import javax.faces.component.html.HtmlInputText;
@FacesComponent(TextBox.COMPONENT_TYPE)
//直接继承JSF基础组件HtmlCommandButton,这样可以直接从父类继承已实现的EditableValueHolder、ClientBehaviorHolder等接口
//EditableValueHolder是实现valueChangeListener功能的接口,ClientBehaviorHolder是实现ajax特性的接口,自己实现太麻烦,直接从父类继承
public class TextBox extends HtmlInputText {
public static final String COMPONENT_FAMILY = "dojo4j.form";
public static final String COMPONENT_TYPE = "dojo4j.form.textBox";
@Override
public String getFamily() {
return COMPONENT_FAMILY;
}
}
TextBoxRenderer.java
package org.dojo4j.component.form;
import java.io.IOException;
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import javax.servlet.http.HttpServletRequest;
import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.renderkit.html_basic.TextRenderer;
@FacesRenderer(componentFamily = TextBox.COMPONENT_FAMILY, rendererType = TextBox.COMPONENT_TYPE)
@ResourceDependencies({
// 引入所需的css样式
@ResourceDependency(library = "dojo/resources", name = "dojo.css", target = "head"),
// 默认采用claro皮肤,以后再采取其他方式换肤
@ResourceDependency(library = "dijit/themes/claro", name = "claro.css", target = "head") })
// 直接继承com.sun.faces.renderkit.html_basic.TextRenderer,修改部分渲染代码
public class TextBoxRenderer extends TextRenderer {
@Override
protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
assert (writer != null);
// 引入dojo.js,因为要加入data-dojo-config属性,因此无法用@ResourceDependency方式,只能输入script标签,以后改用其他方式
writer.startElement("script", component);
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
writer.writeAttribute("src", request.getContextPath() + context.getExternalContext().getRequestServletPath()
+ "/javax.faces.resource/dojo/dojo.js", null);
writer.writeAttribute("data-dojo-config", "async: true, parseOnLoad: true", null);
writer.endElement("script");
// 引入dijit/form/TextBox,略显啰嗦,以后改用其他方式
writer.startElement("script", component);
writer.write("require([ \"dojo/parser\", \"dijit/form/TextBox\" ]);");
writer.endElement("script");
String styleClass = (String) component.getAttributes().get("styleClass");
writer.startElement("input", component);
writeIdAttributeIfNecessary(context, writer, component);
writer.writeAttribute("type", "text", null);
String clientId = component.getClientId(context);
writer.writeAttribute("name", clientId, "clientId");
// 加入id label data-dojo-id data-dojo-type属性
writer.writeAttribute("id", clientId, null);
writer.writeAttribute("data-dojo-id", clientId, null);
writer.writeAttribute("data-dojo-type", "dijit/form/TextBox", null);
if ("off".equals(component.getAttributes().get("autocomplete"))) {
writer.writeAttribute("autocomplete", "off", "autocomplete");
}
if (currentValue != null) {
writer.writeAttribute("value", currentValue, "value");
}
if (null != styleClass) {
writer.writeAttribute("class", styleClass, "styleClass");
}
RenderKitUtils.renderPassThruAttributes(context, writer, component, INPUT_ATTRIBUTES,
getNonOnChangeBehaviors(component));
RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component);
RenderKitUtils.renderOnchange(context, component, false);
writer.endElement("input");
}
// 以下是从com.sun.faces.renderkit.html_basic.TextRenderer复制的private代码
private static final Attribute[] INPUT_ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.INPUTTEXT);
}
textBoxTest.xhtml
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:d4j="http://org.dojo4j">
<h:head>
<meta charset="utf-8" />
<title>TextBox Test</title>
</h:head>
<body class="claro"><!-- 先用claro样式,以后换其他方式换肤 -->
<h:form id="form1" prependId="false"><!-- prependId去掉JSF自动生成的ID前缀,如j_idt1,j_idt2... -->
<d4j:textBox binding="#{textBoxTest.textBox}" />
<d4j:button value="Click Me" actionListener="#{textBoxTest.click}" >
<f:ajax execute="@form" render="text" />
</d4j:button>
<h:outputText id="text" binding="#{textBoxTest.text}"/>
</h:form>
</body>
</html>
TextBoxTest.java
package test;
import javax.faces.bean.ManagedBean;
import javax.faces.component.html.HtmlOutputText;
import org.dojo4j.component.form.TextBox;
@ManagedBean
public class TextBoxTest {
private TextBox textBox;
private HtmlOutputText text;
public void click() {
this.text.setValue(this.textBox.getValue());
}
public TextBox getTextBox() {
return textBox;
}
public void setTextBox(TextBox textBox) {
this.textBox = textBox;
}
public HtmlOutputText getText() {
return text;
}
public void setText(HtmlOutputText text) {
this.text = text;
}
}
预览结果
url:http://localhost:8080/dojo4j/faces/test/textBoxTest.xhtml
下载示例代码
生成的HTML源代码
<!DOCTYPE HTML> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="j_idt2"> <meta charset="utf-8" /> <title>TextBox Test</title> <link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo.css?ln=dojo/resources" /> <link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/claro.css?ln=dijit/themes/claro" /> <script type="text/javascript" src="/dojo4j/faces/javax.faces.resource/jsf.js?ln=javax.faces"></script> </head> <body class="claro"> <!-- 先用claro样式,以后换其他方式换肤 --> <form id="form1" name="form1" method="post" action="/dojo4j/faces/test/textBoxTest.xhtml" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="form1" value="form1" /> <!-- prependId去掉JSF自动生成的ID前缀,如j_idt1,j_idt2... --> <script src="/dojo4j/faces/javax.faces.resource/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true"></script> <script> require([ "dojo/parser", "dijit/form/TextBox" ]); </script> <input type="text" name="j_idt6" id="j_idt6" data-dojo-id="j_idt6" data-dojo-type="dijit/form/TextBox" /> <script src="/dojo4j/faces/javax.faces.resource/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true"></script> <script> require([ "dojo/parser", "dijit/form/Button" ]); </script> <input id="j_idt7" type="submit" name="j_idt7" value="Click Me" id="j_idt7" label="Click Me" data-dojo-id="j_idt7" data-dojo-type="dijit/form/Button" onclick="mojarra.ab(this,event,'action','@form','text');return false" /><span id="text"></span><input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-7323748383932348627:7936551638999269969" autocomplete="off" /> </form> </body> </html>
可以看到整个代码实现和Button差不多,只是继承的父类有所不同,大部分功能都由父类去处理了,实现类只是修改了渲染部分的代码,看起来还是很简单的。
到这里,应该对JSF2.0的组件开发有所了解,如果只是简单的组件应该会开发了。但如果想用dojo打造一款完整的组件库,还要处理一些关键问题,接下来分析一下。
1、由于引用dojo.js的<script>标签要设置data-dojo-config属性,因此无法在Render类上加@ResourceDependency来处理,造成每个组件都生成这段代码,如何避免重复,是一个要处理的问题。
2、每个组件都在生成<script>require([ "dojo/parser", ...... ]);</script>,需要想方法避免重复。
3、现在只能用固定的claro样式,如何解决换肤的问题。
4、我们测试时用的URL全部是以http://.../faces/.../xxx.xhtml路径来访问的,如果以http//.../xxx.faces来访问就可以看到dojo.js没生效,这是相对路径不匹配造成dojo.js无法加载,这个问题也需要解决。
在继续开发更多组件以前,我先解决上面这几个问题。
