最终界面预览
代码目录结构
上面的dojo-1.9.2.jar是dojo js库,自己打包的,建立一个META-INF/resources目录,放置dojo库,打包成jar文件
我选dojo做JSF组件的原因:dojo是一整套完整的javascript ui库,界面美观省去了找美工的麻烦,而且没有许可证限制,随便用。缺点:庞大、复杂、难学。但只要把dojo封装成JSF组件,我相信就可以掩盖90%以上的复杂度,供业务开发人员使用是没问题的。
Button.java
package org.dojo4j.component.form; import javax.faces.component.FacesComponent; import javax.faces.component.html.HtmlCommandButton; @FacesComponent(Button.COMPONENT_TYPE) //直接继承JSF基础组件HtmlCommandButton,这样可以直接从父类继承已实现的ActionSource2、ClientBehaviorHolder等接口 //ActionSource2是实现actionListener功能的接口,ClientBehaviorHolder是实现ajax特性的接口,自己实现太麻烦,直接从父类继承 public class Button extends HtmlCommandButton { public static final String COMPONENT_FAMILY = "dojo4j.form"; public static final String COMPONENT_TYPE = "dojo4j.form.button"; @Override public String getFamily() { return COMPONENT_FAMILY; } }
package org.dojo4j.component.form; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; import javax.faces.application.ResourceDependencies; import javax.faces.application.ResourceDependency; import javax.faces.component.UICommand; import javax.faces.component.UIComponent; import javax.faces.component.behavior.ClientBehavior; import javax.faces.component.behavior.ClientBehaviorContext; 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; @FacesRenderer(componentFamily = Button.COMPONENT_FAMILY, rendererType = Button.COMPONENT_TYPE) // 直接继承com.sun.faces.renderkit.html_basic.ButtonRenderer,修改部分渲染代码 @ResourceDependencies({ //引入所需的css样式 @ResourceDependency(library = "dojo/resources", name = "dojo.css", target = "head"), //默认采用claro皮肤,以后再采取其他方式换肤 @ResourceDependency(library = "dijit/themes/claro", name = "claro.css", target = "head")}) public class ButtonRenderer extends com.sun.faces.renderkit.html_basic.ButtonRenderer { @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { rendererParamsNotNull(context, component); if (!shouldEncode(component)) { return; } String type = getButtonType(component); ResponseWriter writer = context.getResponseWriter(); assert (writer != null); String label = ""; Object value = ((UICommand) component).getValue(); if (value != null) { label = value.toString(); } Collection<ClientBehaviorContext.Parameter> params = getBehaviorParameters(component); if (!params.isEmpty() && (type.equals("submit") || type.equals("button"))) { RenderKitUtils.renderJsfJs(context); } /* 从此处理开始修改 */ //引入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/Button,略显啰嗦,以后改用其他方式 writer.startElement("script", component); writer.write("require([ \"dojo/parser\", \"dijit/form/Button\" ]);"); writer.endElement("script"); writer.startElement("input", component); writeIdAttributeIfNecessary(context, writer, component); String clientId = component.getClientId(context); writer.writeAttribute("type", type, "type"); writer.writeAttribute("name", clientId, "clientId"); writer.writeAttribute("value", label, "value"); //加入id label data-dojo-id data-dojo-type属性 writer.writeAttribute("id", clientId, null); writer.writeAttribute("label", label, null); writer.writeAttribute("data-dojo-id", clientId, null); writer.writeAttribute("data-dojo-type", "dijit/form/Button", null); /* 修改结束 */ RenderKitUtils.renderPassThruAttributes(context, writer, component, ATTRIBUTES, getNonOnClickBehaviors(component)); RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component); String styleClass = (String) component.getAttributes().get("styleClass"); if (styleClass != null && styleClass.length() > 0) { writer.writeAttribute("class", styleClass, "styleClass"); } RenderKitUtils.renderOnclick(context, component, params, null, false); if (component.getChildCount() == 0) { writer.endElement("input"); } } // 以下是从com.sun.faces.renderkit.html_basic.ButtonRenderer复制的private代码 private static final Attribute[] ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.COMMANDBUTTON); private static String getButtonType(UIComponent component) { String type = (String) component.getAttributes().get("type"); if (type == null || (!"reset".equals(type) && !"submit".equals(type) && !"button".equals(type))) { type = "submit"; component.getAttributes().put("type", type); } return type; } private static Map<String, List<ClientBehavior>> getNonOnClickBehaviors(UIComponent component) { return getPassThruBehaviors(component, "click", "action"); } }
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1"> <component> <component-type>dojo4j.form.button</component-type> <component-class>org.dojo4j.component.form.Button</component-class> </component> </faces-config>
<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"> <namespace>http://org.dojo4j</namespace> <tag> <tag-name>button</tag-name> <component> <component-type>dojo4j.form.button</component-type> <renderer-type>dojo4j.form.button</renderer-type> </component> </tag> </facelet-taglib>
<!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>Button Test</title> </h:head> <body class="claro"><!-- 先用claro样式,以后换其他方式换肤 --> <h:form id="form1" prependId="false"><!-- prependId去掉JSF自动生成的ID前缀,如j_idt1,j_idt2... --> <d4j:button value="Click Me" actionListener="#{buttonTest.click}" > <f:ajax execute="@form" render="text" /> </d4j:button> <br/> <h:outputText id="text" binding="#{buttonTest.text}"/> </h:form> </body> </html>
package test; import javax.faces.bean.ManagedBean; import javax.faces.component.html.HtmlOutputText; @ManagedBean public class ButtonTest { private HtmlOutputText text; public void click() { this.text.setValue("Button Test OK!"); } public HtmlOutputText getText() { return text; } public void setText(HtmlOutputText text) { this.text = text; } }
生成的html源代码
<!DOCTYPE HTML> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="j_idt2"> <meta charset="utf-8" /> <title>Button 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"> <form id="form1" name="form1" method="post" action="/dojo4j/faces/test/buttonTest.xhtml" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="form1" value="form1" /> <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_idt5" type="submit" name="j_idt5" value="Click Me" id="j_idt5" label="Click Me" data-dojo-id="j_idt5" data-dojo-type="dijit/form/Button" onclick="mojarra.ab(this,event,'action','@form','text');return false" /> <br /> <span id="text"></span><input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="8461977939671373327:-4614509433011281003" autocomplete="off" /> </form> </body> </html>