您的位置:首页 > Web前端 > JavaScript

JSF2.0实战 - 7、自定义<h:head>

2014-05-13 12:17 519 查看
在继续开发新的组件前,先把前面遇到的问题解决

接上篇,如果用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>


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

下载代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: