Java Enterprise Edition 5 平台包括 JavaServer Faces (JSF) 技术,该技术提供了成熟的可扩展的用户界面组件模型。通过使用此模型的设计,应用程序开发者可通过扩展 JSF 中包含的标准组件来方便地创建定制组件,并在应用程序中重用这些组件。本文讨论了以下策略:引入 Servlet 来处理 AJAX 请求以在 JSF 组件中添加 AJAX 支持。有关访问静态和动态资源的各种方法的详细信息,请参见“访问静态和动态资源”。有关替代解决方案和相关解决方案,请参见“结合使用 JSF 和 AJAX”。
如果开发者要在其 JSF 组件中加入 AJAX 支持,他们可以从多个策略中进行选择。一种解决此问题的方法是,使用 Servlet 截取并实现定制组件的 AJAX 请求。定制 JSF 组件所需的资源与应用程序包打包在一起,可以直接对其进行访问。
我们将讨论使用 Servlet 将 AJAX 支持加入到 JSF 应用程序中的策略。这包括创建一个定制组件以生成所需的 Javascript ,该 Javascript 用于在客户端执行定制组件与另一个服务器端组件的 AJAX 交互,提供 PhaseListener 形式的 AJAX 请求
这种方法可用于以下情况:
这种方法自身也存在一些问题,如下所示:
出于以上原因,我们目前建议的方法是使用第三方库(“访问静态和动态资源”文章中详细介绍了这种方法)。我们仍在研究一些新出现的技术,当我们的建议发生变化时,将会对这些文章进行更新。
有关此组件功能的简单示例,请参见 standalone.jsp,其中包含单个 JSP 页面中的所有脚本和功能。
/bp-jsf-example/CompAServlet?itemId=test1A2) Servlet 接收请求,检索请求的 itemId 参数以及创建/发送相应的响应。
由于我们的组件是用于显示详细信息的弹出式组件,因此,我们需要呈现某个标记,以用于区分出实际所显示的部分。可以使用多种方法来完成此操作,一种方法是在呈现器中对标记进行固定编码,从而设置维护名称空间约定所需的值。此处,在前部添加组件 ID 以防止组件的多个实例发生冲突。组件开发者也可以使用 ResponseWriter 的 startElement 和 endElement 方法,直接在呈现器中对模板进行编码。如果要使用工具对组件进行处理(以便在 IDE 中以可视方式使用),则这可能是最佳的方法,因为每个 startElement 调用都与组件相关联。这样,工具便可以更好地跟踪属于各个组件的标记。Map requestMap=context.getExternalContext().getRequestMap();
Boolean scriptRendered=(Boolean)requestMap.get(RENDERED_SCRIPT_KEY);
// Check to see if resource already rendered
if (scriptRendered != null && scriptRendered.equals(Boolean.TRUE)) {
return;
}
// put flag in requestMap to indicate already rendered
requestMap.put(RENDERED_SCRIPT_KEY, Boolean.TRUE);
String contextRoot=context.getExternalContext().getRequestContextPath();
// Render markup tag for the javascript file
writer.startElement("script", component);
writer.writeAttribute("type", "text/javascript", null);
writer.writeAttribute("src", contextRoot + "/compA.js", null);
writer.endElement("script");
writer.write("\n");
writer.write("<div class=\"bpui_compA_popTop\">");
writer.write(" <div class=\"bpui_compA_cornerTL\"><div class=\"bpui_compA_cornerTR\">");
writer.write(" <center><font color=\"white\"><b><span id=\"" + clientId + "_title\">title</span></b></font></center>");
writer.write(" </div></div>");
writer.write("</div>");
writer.write("<div class=\"bpui_compA_popMid\">");
writer.write(" <table border=\"0\" style=\"width: 100%\">");
writer.write(" <tr>");
writer.write(" <td>");
writer.write(" <table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"5\" cellspacing=\"5\">");
writer.write(" <tr>");
writer.write(" <td><span id=\"" + clientId + "_message\">Value</span></td>");
writer.write(" </tr>");
writer.write(" </table>");
writer.write(" </td>");
writer.write(" </tr>");
writer.write(" </table>");
writer.write("</div>");
writer.write("<div class=\"bpui_compA_popBot\">");
writer.write(" <div class=\"bpui_compA_cornerBL\"><div class=\"bpui_compA_cornerBR\">");
writer.write(" </div></div>");
writer.write("</div>");
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/xml;charset=UTF-8");
PrintWriter out = response.getWriter();
String itemId=request.getParameter("itemId");
response.setHeader("Pragma", "No-Cache");
response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
response.setDateHeader("Expires", 1);
if(itemId != null) {
out.println("<response>");// custom response code goes here
...
out.println("</response>");
} else {
out.println("<response>");
out.println("<title><![CDATA[REQUEST ERROR]]></title>");
out.println("<message><![CDATA[The query parameter 'itemId' required]]></message>");
out.println("</response>");
}
out.flush();
out.close();
}
<!-- register the custom renderer for the standard component family -->
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>CompA</renderer-type>
<renderer-class>com.sun.javaee.blueprints.components.ui.example.CompARenderer</renderer-class>
</renderer>
</render-kit>
<ui:compA id="pop1" url="CompAServlet?itemId="/>
<a href="#" onmouseover="bpui.compA.showPopup('pop1', event, 'test1A')"
onmouseout="bpui.compA.hidePopup('pop1')" style="cursor: pointer"><b>Mouse over link to see popup (test1A)</b></a><br/>
<small><i>(With a Servlet fulfilling AJAX Request)</i></small><br/><br/>>
<div id="pop1" class="bpui_compA_popup">
<div class="bpui_compA_popTop">
<div class="bpui_compA_cornerTL"><div class="bpui_compA_cornerTR">
<center>
<font color="white">
<b><span id="pop1_title">title</span></b></font>
</center>
</div></div>
</div>
<div class="bpui_compA_popMid">
<table border="0" style="width: 100%">
<tr>
<td>
<table border="0" bgcolor="#ffffff" cellpadding="5" cellspacing="5">
<tr>
<td><span id="pop1_message">Value</span></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div class="bpui_compA_popBot">
<div class="bpui_compA_cornerBL"><div class="bpui_compA_cornerBR">
</div></div>
</div>
</div>
<script type="text/javascript">
bpui.compA['pop1']=new bpui.compA.createPopup('pop1', '/bp-jsf-example/CompAServlet?itemId=');
</script>
请注意,Javascript 代码创建新的 bpui.compA.createPopup 对象并将结果存储在 bpui.compA 名称空间的 "pop1" 属性下面。此对象是一个 Javascript 关闭,通过 bpui.compA.showPopup 函数触发弹出式组件时将检索此对象(如下所述)。
// create a closure to maintain the component's id and AJAX url with callback function
bpui.compA.createPopup=function(componentId, urlx) {
this.componentId=componentId;
this.urlx=urlx;
this.ajaxReturnFunction=function() {
// make sure response is ready
if (bpui.compA.req.readyState == 4) {
// make sure it is a valid response
if (bpui.compA.req.status == 200) {
// populate the popup with the info from the response
var resultx=bpui.compA.req.responseXML.getElementsByTagName("response")[0];
document.getElementById(componentId + "_title").innerHTML=resultx.getElementsByTagName("title")[0].childNodes[0].nodeValue;
document.getElementById(componentId + "_message").innerHTML=resultx.getElementsByTagName("message")[0].childNodes[0].nodeValue;;
// show popup with the newly populated information
document.getElementById(componentId).style.visibility='visible';
} else if (bpui.compA.req.status == 204){
// error, just show alert
alert("204 returned from AJAX call");
}
}
}
}
bpui.compA.showPopup=function(popupx, eventx, itemId) {
// Position popup base on event and set timeout so popup isn't flashing all the time
var xx=0;
var yy=0;
if (!eventx) var eventx=window.event;
if (eventx.pageX || eventx.pageY){
xx=eventx.pageX;
yy=eventx.pageY;
} else if (eventx.clientX || eventx.clientY) {
xx=eventx.clientX + document.body.scrollLeft;
yy=eventx.clientY + document.body.scrollTop;
}
document.getElementById(popupx).style.left= (xx + 3) + "px";
document.getElementById(popupx).style.top=yy + "px";
// make sure the popup doesn't show all the time, need to mouseover for at least 1 second.
bpui.compA.timeout=setTimeout("bpui.compA.showPopupInternal('" + popupx + "', '" + itemId + "')", 1000);
}
bpui.compA.showPopupInternal=function(popupx, itemId) {
// initialize the AJAX request
bpui.compA.req=bpui.compA.initRequest();
// retrieve the correct popup object that is being shown
popObject=bpui.compA[popupx];
// concatenate the itemId value to the URI
url=popObject.urlx + escape(itemId);
// set the correct popup's callback function
bpui.compA.req.onreadystatechange = popObject.ajaxReturnFunction;
bpui.compA.req.open("GET", url, true);
// send the request
bpui.compA.req.send(null);
}