JSF2.0实战 - 6、TextBox

先上代码

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

JSF2.0实战 - 6、TextBox_第1张图片


下载示例代码


生成的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无法加载,这个问题也需要解决。

在继续开发更多组件以前,我先解决上面这几个问题。

你可能感兴趣的:(框架,前端,JSF,dojo,JSF2.0)