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

JSF2.0实战 - 10、自定义Ajax更新方式

2014-05-19 23:15 405 查看
前面实现了简单的组件开发,但是组件对ajax支持的不够好,看这个代码

<d4j:textBox binding="#{textBoxTest2.textBox}" />
<d4j:button value="Click Me" actionListener="#{textBoxTest2.click}">
<f:ajax execute="@form" render="textBox2" />
</d4j:button>
<d4j:textBox id="textBox2" binding="#{textBoxTest2.textBox2}" />




目标效果是,在左边的textBox中输入文字,点击按钮后文字显示在右边的textBox中,但是实际上效果和预计的有差距



这是因为JSF的ajax更新只能替换目标ID的HTML元素,这种方式对基本的HTML控件有效果,但是我们的textBox是用js渲染出来的,所以必须用js代码来控制页面更新。

有两种方案解决,一种是将组件的ID渲染到一个<span>的标签上,<span>内再包含<script>,这样更新时替换<span>块,也就替换了<script>块,便可以执行所需的js代码。第二种是替换jsf.js。修改里面处理ajax更新的代码。

第一种方案,修改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 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({
@ResourceDependency(name = "dijit/form/TextBox.js", 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);

//!context.getPartialViewContext().isPartialRequest()是非ajax方式渲染,说明组件在页面首次打开时渲染
if (!context.getPartialViewContext().isPartialRequest()) {
String styleClass = (String) component.getAttributes().get("styleClass");
writer.startElement("input", component);
//注释掉这一句,否则会给input设置ID属性
//			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 + "_obj", null);//修改ID,与script块的ID区分开
writer.writeAttribute("data-dojo-id", clientId + "_obj", 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");

//生成一个span,专门用来做ajax更新用,把组件的ID赋给span块
writer.startElement("span", component);
writer.writeAttribute("id", clientId, null);
writer.writeAttribute("style", "display:none;", null);
writer.endElement("span");
} else {
//ajax响应时返回的代码,jsf会替换目标ID的HTML元素,即上面生成的span元素
String clientId = component.getClientId(context);
TextBox textBox = (TextBox) component;
Object value = textBox.getValue();

writer.startElement("span", component);
writer.writeAttribute("id", clientId, null);
writer.writeAttribute("style", "display:none;", null);
writer.startElement("script", component);
writer.writeAttribute("type", "text/javascript", null);
//用脚本变更dojo控件的属性值
//writer.write("dijit.registry.byId('"+clientId+"_obj').set('value', '" + (value == null ? "" : value) + "');");
writer.write("setValue(\"" + (value == null ? "" : value) + "\");");
writer.endElement("script");
writer.endElement("span");
}
}

// 以下是从com.sun.faces.renderkit.html_basic.TextRenderer复制的private代码
private static final Attribute[] INPUT_ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.INPUTTEXT);
}


这种方式的缺陷是把组件的ID赋给了纯粹为了ajax更新才产生的<span>标签,而页面上真正的控件却被迫用另外的ID,当需要用javascript控制真正的控件时因为ID的原因容易混淆出错。另一种方案不会有这种缺陷,就是替换jsf.js文件。

为了不对JSF原有的jsf.js文件造成侵入,我复制一份jsf.js文件放在自己的项目中,进行修改,并保证修改结果既能满足我们的需要,又不影响原有的功能。目前JSF2.0的ajax仅实现了update,其余的delete、insert、attributes、eval、extension虽然在jsf.js中实现了,但在后面java中没有实现,也不知道以后会不会实现,因此目前只能修改update中的代码,判断更新的组件是否是我们自定义的组件,如果是,则直接运行ajax返回的结果,如果不是,则按原来的代码继续执行。

src/META-INF/resources/javax.faces/dojo4j-jsf-uncompressed.js

......

} else if (id === "javax.faces.ViewHead") {
throw new Error("javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents");
} else {
if (window.dijit && dijit.registry && dijit.registry.byId) {
var d = dijit.registry.byId(id);
if (d) {
scripts = stripScripts(src);
runScripts(scripts);
return;
}
}
var d = $(id);
if (!d) {
throw new Error("During update: " + id + " not found");
}

......

jsf.separatorchar = ':';

jsf.specversion = 22000;


还要在filter中做修改,当系统访问到jsf.js时,返回我们修改过的jsf.js文件内容,而不是JSF2.0原来的jsf.js文件。

DojoFilter.java

if (uri.contains(ResourceHandler.RESOURCE_IDENTIFIER)) {
// 替换jsf.js
String jsfjs = null;
if (uri.indexOf(ResourceHandler.RESOURCE_IDENTIFIER + "/jsf.js") > -1) {
jsfjs = ResourceHandler.RESOURCE_IDENTIFIER + "/dojo4j-jsf"
+ ("Development".equals(request.getParameter("stage")) ? "-uncompressed.js" : ".js");
}

String facesServletMapping = getFacesMapping(request);
// 只处理facesServletMapping为.xxx后缀的情况,不处理facesServletMapping为/xxx为前缀的情况
if (!Util.isPrefixMapped(facesServletMapping)) {
// 只有从引用资源的页面URL中才能取得Faces Servlet的后缀
String referer = request.getHeader("Referer");
if (referer != null && referer.trim().length() > 0) {
if (referer.indexOf('?') > -1) {
referer = referer.substring(0, referer.indexOf('?'));
}
if (referer.indexOf('.') > -1) {
String subfix = referer.substring(referer.lastIndexOf('.'));

if (jsfjs == null) {
// 给没有Faces Servlet的后缀的资源加上后缀,确保通过JSF框架来解析资源文件
if (!facesServletMapping.equals(subfix)) {
String url = request.getServletPath() + subfix;
// System.out.println(url);
request.getRequestDispatcher(url).forward(request, response);
return;
}
} else {
jsfjs = jsfjs + subfix;
}
}
}
} else {
if (jsfjs != null) {
jsfjs = facesServletMapping + jsfjs;
}
}

if (jsfjs != null) {
request.getRequestDispatcher(jsfjs).forward(request, response);
return;
}
}

chain.doFilter(req, resp);
}


TextBoxRenderer.java

if (!context.getPartialViewContext().isPartialRequest()) {
String styleClass = (String) component.getAttributes().get("styleClass");
writer.startElement("input", component);

......

writer.endElement("input");
} else {
//ajax更新时输出<script type="text/javascript">......</script>代码块,页面会执行块中的javascript代码
String clientId = component.getClientId(context);
TextBox textBox = (TextBox) component;
Object value = textBox.getValue();
writer.startElement("script", component);
writer.writeAttribute("type", "text/javascript", null);
writer.write("dijit.registry.byId(\"" + clientId + "\").set(\"value\", " + (value == null ? "null" : "\"" + value + "\"") + ");");
writer.endElement("script");
}


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