JSF2.0实战 - 7、自定义

在继续开发新的组件前,先把前面遇到的问题解决

接上篇,如果用IE访问,是这个样子


可以看到按钮不好看,这是因为生成的css是这样的路径:/dojo4j/faces/javax.faces.resource/dojo.css?ln=dojo/resources,这会导致css中的图片相对路径不正确,无法加载图片,要解决这个问题,只有让css变成/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css才行。修改组件Renderer类中的@ResourceDependency,不用JSF建议的library="xxx", name="xxx"方式,而是只用name="xxx"。

修改代码为:

@ResourceDependencies({
	@ResourceDependency(name = "dojo/resources/dojo.css", target = "head"), 
	@ResourceDependency(name = "dijit/themes/claro/claro.css", target = "head")})


可以看到按钮加载了背景图片,要好看一些了。后面我们做的组件都会采用这种方式,即只用name,不用library。因为library除了在url后面生成一个?ln=xxx,几乎没什么用处。

下面处理这两个问题:

1、由于引用dojo.js的<script>标签要设置data-dojo-config属性,因此无法在Render类上加@ResourceDependency来处理,造成每个组件都生成这段代码
2、每个组件都在生成<script>require([ "dojo/parser", ...... ]);</script>

避免重复生成script代码最简单的解决方案就是自定义<h:head>标签,加载必需的代码,判断组件引用并过滤重复内容。这样做的好处是可以把必需引用的js文件写在head中,不需要每个组件再重复生成代码,缺点就是页面内不适合再用其他的js框架或JSF框架,那样容易引起冲突。


Head.java

package org.dojo4j.component;

import javax.faces.component.FacesComponent;
import javax.faces.component.html.HtmlHead;

@FacesComponent(Head.COMPONENT_TYPE)
//直接继承JSF基础组件HtmlHead
public class Head extends HtmlHead {

	public static final String COMPONENT_FAMILY = "dojo4j.component";
	
	public static final String COMPONENT_TYPE = "dojo4j.component.head";

	@Override
	public String getFamily() {
		return COMPONENT_FAMILY;
	}
}

HeadRenderer.java

package org.dojo4j.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import javax.servlet.http.HttpServletRequest;

@FacesRenderer(componentFamily = Head.COMPONENT_FAMILY, rendererType = Head.COMPONENT_TYPE)
//直接继承com.sun.faces.renderkit.html_basic.HeadRenderer,修改部分渲染代码
public class HeadRenderer extends com.sun.faces.renderkit.html_basic.HeadRenderer {

	@Override
	public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
		HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
		ResponseWriter writer = context.getResponseWriter();
		
		//加入dojo.css引用,因为每个dojo组件都需要dojo.css支持,所以加到head里避免重复生成
		writer.startElement("link", component);
		writer.writeAttribute("type", "text/css", null);
		writer.writeAttribute("rel", "stylesheet", null);
		writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath()
				+ "/javax.faces.resource/dojo/resources/dojo.css", null);
		writer.endElement("link");
		
		//加入dojo.js引用,因为每个dojo组件都需要dojo.js支持,所以加到head里避免重复生成
		writer.startElement("script", component);
		writer.writeAttribute("type", "text/javascript", null);
		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");
		encodeHeadResources(context, component);
		writer.endElement("head");//writer.startElement("head");在父类的encodeBegin里
	}
	
	
	private void encodeHeadResources(FacesContext context, UIComponent component) throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		
		List<String> requires = new ArrayList<String>();
		//require加入必需的dojo/parser
		requires.add("dojo/parser");

		UIViewRoot viewRoot = context.getViewRoot();
		for (UIComponent resource : viewRoot.getComponentResources(context, "head")) {
			Map<String, Object> attributes = resource.getAttributes();
			String name = (String) attributes.get("name");
			
			//把引用的dojo库中的js文件,转换成require写法,不引入js文件
			if (name.startsWith("dojo/") || name.startsWith("dijit/") || name.startsWith("dojox/")) {
				if (name.endsWith(".js")) {
					String path = name.substring(0, name.lastIndexOf("."));
					if (!requires.contains(path))
						requires.add(path);
					continue;
				}
			}
			resource.encodeAll(context);
		}

		//生成dojo require方式的代码
		writer.startElement("script", component);
		writer.write("require([");
		String tmp = "";
		for (int i = 0; i < requires.size(); i++) {
			writer.write(tmp + "\"" + requires.get(i) + "\"");
			tmp = ", ";
		}
		writer.write("]);");
		writer.endElement("script");
	}
}

faces-config.xml

<component>
	<component-type>dojo4j.component.head</component-type>
	<component-class>org.dojo4j.component.Head</component-class>
</component>

dojo4j.taglib.xml

<tag>
	<tag-name>head</tag-name>
	<component>
            <component-type>dojo4j.component.head</component-type>
            <renderer-type>dojo4j.component.head</renderer-type>        
        </component>
</tag>

修改TextBoxRenderer.java,删除下面这部分代码

// 引入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");  

修改@ResourceDependencies为

@ResourceDependencies({
	// 默认采用claro皮肤,以后再采取其他方式换肤
	@ResourceDependency(name = "dijit/themes/claro/claro.css", target = "head"), 
	@ResourceDependency(name = "dijit/form/TextBox.js", target = "head")})

这样,组件共用的代码都放置在head中,避免每个组件重复编写代码

修改textBoxTest.xhtml,把<h:head>换成<d4j:head>

<d4j:head>
	<meta charset="utf-8" />
	<title>TextBox Test</title>
</d4j:head>


生成的textBoxTest.xhtml源代码

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://java.sun.com/jsf/passthrough">
<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/resources/dojo.css" />
<script type="text/javascript" src="/dojo4j/faces/javax.faces.resource/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true"></script>
<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dijit/themes/claro/claro.css" />
<script type="text/javascript" src="/dojo4j/faces/javax.faces.resource/jsf.js?ln=javax.faces"></script>
<script>
	require([ "dojo/parser", "dijit/form/TextBox", "dijit/form/Button" ]);
</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... -->
		<input type="text" name="j_idt6" id="j_idt6" data-dojo-id="j_idt6" data-dojo-type="dijit/form/TextBox" /><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="-2582773261651117086:1796796122005148629" autocomplete="off" />
	</form>
</body>
</html>


可以看到最终页面上生成的代码优化了很多


下载代码

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