先上代码
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; } }
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); }
<!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>
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>
到这里,应该对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无法加载,这个问题也需要解决。
在继续开发更多组件以前,我先解决上面这几个问题。