最终界面预览
代码目录结构
上面的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;
}
}
ButtonRenderer.java
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");
}
}
faces-config.xml
<?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>
dojo4j.taglib.xml
<?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>
buttonTest.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>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>
ButtonTest.java
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;
}
}
url:http://localhost:8080/dojo4j/faces/test/buttonTest.xhtml
生成的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"
οnclick="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>
下载示例代码