在继续开发新的组件前,先把前面遇到的问题解决
接上篇,如果用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>
<!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>
可以看到最终页面上生成的代码优化了很多
下载代码