DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> AJAX入門 >> AJAX基礎知識 >> 基於AJAX和JSF打造豐富的互聯網組件
基於AJAX和JSF打造豐富的互聯網組件
編輯:AJAX基礎知識     

 

  在本篇中,我們將向你展示怎樣使用Mabon來創建一個簡單而強有力的輸入組件,它具有類似於Google Suggest所提供的內置的建議功能。為了使Web開發者更為容易地使用我們的JDJ InputSuggest組件,我們借助於Weblets開源工程來把外部資源,例如圖標和JavaScript庫,綁定到一個Java檔案文件(JAR)中—由它來描述我們的JSF組件綁定。

 

  一、創建支持AJAX的JSF HtmlInputSuggest組件

 

  這個JSF AJAX輸入建議方案共包括四個類,見圖1。

 

圖1.類圖:構建輸入建議組件所需要的類

這些類分別是:

•HtmlInputSuggest—屏幕生成器特定的子類。

•HtmlRenderer—這是一個超類,它提供一些便利的方法來實現資源編碼。

•HtmlInputSuggestRenderer—是你的新定制的屏幕生成器,它負責把標注生成到客戶端屏幕上,包括需要的資源,例如JavaScript庫和式樣表等。

•HtmlInputSuggestTag是標簽處理器。

在我們的輸入建議解決方案中,我們實現了一個JavaScript庫—inputSuggest.js—它包含利用Mabon從Web開發者的支持bean中檢索數據的功能。在本文中,我們將詳細討論inputSuggest.js文件和HtmlInputSuggestRenderer—它們都受Mabon的影響並且提供了輸入域(這些輸入域都具有輸入探測(type-ahead)和建議列表功能)。

二、輸入建議JavaScript庫

既然我們使用Mabon,因此不需要擔心從支持bean中取回數據的問題。我們可以把這項任務交給Mabon來完成。然而,我們關心的是,如何處理XMLHttpRequest對象返回的數據,如何填充實際的建議列表以及如何處理用戶交互。這個inputSuggest.js庫中包含了大量的函數,用來處理鍵盤導航和鼠標交互。篇幅所限,在此我們將集中分析對該JSF HtmlInputSuggest組件有重大影響的函數。

(一)doKeyPress函數

顯示於列表1中的doKeyPress函數負責處理鍵擊事件並檢查是否用戶按下了TAB鍵。在正常情況下,這個TAB鍵將移出輸入域並激發blur事件。對於本文中的輸入建議解決方案來說,一次TAB鍵擊也可以用於從建議列表中選擇一個活動行。為此,我們需要跟蹤TAB鍵,從建議列表中選擇一行,把值添加到輸入域,或者,如果沒有列表數據可用的話,離開該輸入域。如果發生控件導航,那麼將激活doBlur()函數並關閉建議列表。

列表1—doKeyPress函數

projsf.jdj.doKeyPress = function(event){

var input = (event.srcElement || event.target);

var inputId = input.id;

var div = document.getElementById(inputId + "$suggest");

var divStyle = (div.currentStyle || div.style);

if (event.keyCode == 9 && divStyle.display == "block")

{

div.style.display = "none";

var activeRow = projsf.jdj._findActiveRow(div);

input.value = activeRow.innerHTML;

return false; //取消按Tab鍵離開輸入域

}

return true; //繼續:按Tab鍵離開輸入域,它將調用doBlur()

}

列表2—doKeyUp函數

projsf.jdj.doKeyUp = function(event){

var input = (event.srcElement || event.target);

var inputId = input.id;

var div = document.getElementById(inputId + "$suggest");

if (event.keyCode == 9)//Tab鍵

{ return false; }

else if ((div.style.display == "block" || div.childNodes.length > 0) &&

(event.keyCode == 40 || event.keyCode == 38))

{

if (div.style.display == "none")

{ div.style.display = "block"; }

else {

var activeRow = projsf.jdj._findActiveRow(div);

switch (event.keyCode) {

case 40: /向下箭頭

if (activeRow.nextSibling)

{

activeRow.className = "HtmlInputSuggestRow";

activeRow = activeRow.nextSibling;

activeRow.className = "HtmlInputSuggestActiveRow";

}

break;

case 38: /向上箭頭

if (activeRow.previousSibling)

{

activeRow.className = "HtmlInputSuggestRow";

activeRow = activeRow.previousSibling;

activeRow.className = "HtmlInputSuggestActiveRow";

}

break;

}

input.value = activeRow.innerHTML;

}

return false;

}

if (event.keyCode != 8)//不是一個Backspace鍵

{

input.blur();

input.focus();

}

if (input.value.length <= 2)

div.style.display = "none";

}

 

 

 

列表3—doChange函數

projsf.jdj.doChange = function(event,doSuggestURL){

var input = (event.srcElement || event.target);

var inputId = input.id;

var context = { _inputId: inputId };

net.java.dev.mabon.send({ url: doSuggestURL,

args: [input.value],

callback: function(result) {

projsf.jdj._callback.call(context,result);} });

return true;

}

列表4:_callback函數

projsf.jdj._callback = function(results){

var inputId = this._inputId;

var input = document.getElementById(inputId);

var div = document.getElementById(inputId + "$suggest");

if (results.length <= 1) {

div.style.display = "none";

return;

}

//從上下文中得到輸入域ID

var input = document.getElementById(inputId);

div.style.width = input.offsetWidth;

while (div.firstChild) {

div.removeChild(div.firstChild);

}

for (var i=0; i < results.length; i++) {

var row = document.createElement("div");

var span = document.createElement("span");

var text = document.createTextNode(results[i]);

row.className = "HtmlInputSuggestRow";

row.appendChild(text);

row.onmouseover = new Function("event",

"projsf.jdj._doMouseOver(event ||

window.event)");

row.onclick = new Function("event",

"projsf.jdj._doMouseClick(event ||

window.event)");

div.appendChild(row);

}

div.firstChild.className = "HtmlInputSuggestActiveRow";

div.style.display = "block";

window.setTimeout("projsf.jdj._selectText('" + inputId + "', " +

"'" + input.value + "', " +

"'" + results[0] + "')",

200);

}

列表5:_selectText函數

projsf.jdj._selectText=function(inputId,initialValue,suggestion){

var input = document.getElementById(inputId);

if (input.value != initialValue)

return;

if (input.value == suggestion) return;

if (input.createTextRange)//IE特定的

{

var selectionStart = input.value.length;

input.value = suggestion;

var range = input.createTextRange();

range.moveStart("character", selectionStart);

range.moveEnd("character", input.value.length);

range.select();

}

else //DOM兼容的

{

var selectionStart = input.value.length;

input.value = suggestion;

input.selectionStart = selectionStart;

input.selectionEnd = input.value.length;

}

}

列表6.HtmlInputSuggestRenderer的encodeBegin()方法

package com.apress.projsf.jdj.render.html;

Import ...//(省略)

/**

*HtmlInputSuggestRenderer用自動建議的行為生成一個傳統的HtmlInputText域

*.

*/

public class HtmlInputSuggestRenderer extends HtmlRenderer{

//...

public static String TITLE_ATTR = "title";

public static String DO_SUGGEST_ATTR = "doSuggest";

public void encodeBegin(

FacesContext context,

UIComponent component) throws IOException

{

writeScriptResource(context,

"weblet://org.dojotoolkit.browserio/dojo.js");

writeScriptResource(context,

"weblet://net.java.dev.mabon/mabon.js");

writeScriptResource(context,

"weblet://com.apress.projsf.jdj/inputSuggest.js");

writeStyleResource(context,

"weblet://com.apress.projsf.jdj/inputSuggest.css");

}

 

 

 

列表7.HtmlInputSuggestRenderer的encodeEnd()方法

public void encodeEnd(

FacesContext context,

UIComponent component) throws IOException

{

String valueString = _getValueAsString(context, component);

String clientId = component.getClientId(context);

Map attrs = component.getAttributes();

String title = (String)attrs.get(TITLE_ATTR);

String onchange = (String)attrs.get(ONCHANGE_ATTR);

MethodBinding doSuggest = (MethodBinding)attrs.get(DO_SUGGEST_ATTR);

ResponseWriter out = context.getResponseWriter();

out.startElement("div", component);

if (title != null)

out.writeAttribute("title", title, TITLE_ATTR);

//
// value="[converted-value]" onchange="[onchange]" />

out.startElement("input", component);

out.writeAttribute("id", clientId, null);

out.writeAttribute("name", clientId, null);

if (valueString != null)

out.writeAttribute("value", valueString, null);

if (doSuggest != null)

{

//當使用服務器端建議時,禁止浏覽器自動完成功能

out.writeAttribute("autocomplete", "off", null);

String expression = doSuggest.getExpressionString();

//從表達式中修整#{}

String bindingRef =

expression.substring(2, expression.length() - 1);

ViewHandler handler =

context.getApplication().getViewHandler();

String doSuggestURL =

handler.getResourceURL(context, "mabon:/" + bindingRef);

out.writeAttribute("onkeypress",

"return projsf.jdj.doKeyPress(event);", null);

out.writeAttribute("onkeyup",

"return projsf.jdj.doKeyUp(event);", null);

out.writeAttribute("onchange",

"projsf.jdj.doChange(event, '" + doSuggestURL

+ "');", null);

out.writeAttribute("onblur",

"return projsf.jdj.doBlur(event);", null);

}

out.endElement("input");

out.startElement("br", null);

out.endElement("br");

out.startElement("div", null);

out.writeAttribute("id", clientId + "$suggest", null);

out.writeAttribute("class", "HtmlInputSuggest", null);

out.endElement("div");

}

列表8.一個使用JSF HttpInputSuggest組件的JSP頁面




xmlns:jdj="http://projsf.apress.com/jdj"

xmlns:f="http://java.sun.com/jsf/core"

xmlns:h="http://java.sun.com/jsf/html" >


















title="Input Suggest Component"

value="#{backingBean.value}"

doSuggest="#{backingBean.doSuggest}" />









列表9:支持bean的value屬性

package com.apress.projsf.jdj.application;

import java.util.ArrayList;

import java.util.List;

/**

*BackingBean是一個inputSuggest.jspx文檔的支持bean。

*/

public class BackingBean{

public void setValue(Object value) { _value = value; }

public Object getValue() { return _value; }

列表10.支持bean的doSuggest()方法

public String[] doSuggest(String initialValue) {

List suggestions = new ArrayList();

for (int i=0; i < _MASTER_LIST.length; i++) {

if (_MASTER_LIST[i].startsWith(initialValue))

suggestions.add(_MASTER_LIST[i]);

}

return suggestions.toArray(new String[0]);

}

private Object _value;

static private final String[] _MASTER_LIST = new String[]

{

"Pro JSF and Ajax",

"Pro Ajax",

"Pro JSP 2",

"Pro Jakarta",

"Pro J2EE 1.4"

};

}

 

 

一個JavaScript工具函數。我們可以使用這種Mabon協議來發送目標URL和需要的任何參數,然後異步地從托管bean中接收數據。

(七)Mabon和JSON

正如你所知,XMLHttpRequest提供了兩種響應類型—responseText和responseXML—它們可以用於取回數據。你可能會問:我何時該使用哪一種響應類型?其實,這個問題的答案依賴於是否由你自己控制響應的語法。

responseXML類型返回一個完整的DOM對象(它提供多種方式來遍歷這棵DOM樹),從而允許你查找需要的信息並把所作變化應用到當前文檔中。當你的組件有可能影響到周圍的元素而且你不能控制響應時(例如,當你與一個Web服務進行通訊時),這是相當有用的。

對於本例中的輸入建議組件,你的確要控制響應並且你只想從你的組件中取回數據,而不是修改整個頁面的DOM結構。responseText類型返回普通的文本,這允許你利用JSON語法用於響應。為了在組件中利用AJAX技術,JSON是一種極其有用的數據交換格式,因為它可以輕易地使用eval()函數進行分析。

eval()函數僅使用一個參數(一個JavaScript代碼字符串),並一次性分析和執行這個字符串而不是分析處理每一部分。這種方法要比任何其它類型的分析(例如XML DOM分析方法)快得多。

這正是為什麼Mabon實現JSON的原因—你能夠控制響應,而且JSON具有語法簡單和分析速度快的特點。

(八)encodeEnd()方法

真正的工作是在encodeEnd()方法中完成的,見列表7。在encodeEnd()方法中,我們從HtmlInputSuggest組件得到屬性的Map。這個組件的屬性之一是doSuggest屬性。通過這個屬性,我們能夠得到MethodBinding(如果有的話),並且從這個MethodBinding對象,我們能夠得到實際的由Web開發者所定義的MethodBinding表達式(例如,#{backingBean.doSuggest})。然後,我們從表達式中修整#{}並且用類似mabon:/協議的語法來連接字符串的余下部分。最後,MabonViewHandler將識別這個字符串並返回一個資源URL—它將被寫向客戶端(例如,/context-root/mabon-servlet-mapping/backingBean.doSuggest)。

三、使用輸入建議組件

創建一套AJAX方案並不是一項簡單的任務,盡管有若干使得這類工作更為容易些的AJAX工具包可用(例如Dojo Toolkit,www.dojotoolkit.org)。相比之下,JSF提供的是一種更為簡單的編程模型和一種為大量開發者所熟悉的工具:JSP和Java。為了完整地結束本文中所提供的Ajax解決方案,讓我們分析一下你如何在一個JSF應用程序中使用這個輸入建議組件,由列表8所示。

這個頁面包含一個HtmlInputSuggest組件(),它把value屬性設置為一個綁定表達式的值。這個表達式指向一個支持bean的value屬性。doSuggest屬性包含一個綁定表達式的方法—這個表達式指向在同一個支持bean上的doSuggest()方法。你可以在列表9中觀察這個支持bean。

value屬性僅是一個普通的JavaBean屬性。但是,顯示於列表10中的doSuggest()方法卻值得引起你的注意。這個方法使用由用戶輸入的初始值,該值是從doChange()函數(見列表3)中經由Mabon傳遞給它的。然後,doSuggest()方法根據用戶在客戶端輸入的初始值返回一個經過過濾的數組。值得注意的是,返回的值遵循支持的JSON語法。

這個HtmlInputSuggest組件的最後結果顯示於圖2中。

 

圖2.在一個浏覽器中生成HtmlInputSuggest組件

四、結論

從本文中,我們希望你已經理解了如何使用Mabon來實現你的JSF組件以支持基於Ajax技術的數據回取,以及怎樣通過利用Weblets工程把你的JSF組件需要的外部資源打包到與你的Java類相同的文檔中。

最後,既然你已經知道怎樣利用JSF和AJAX技術創建可重用的豐富互聯網應用程序,那麼我們非常希望你能夠利用你在本系列文章中所學的技術來創建自己的定制組件以構建豐富互聯網應用程序(RIA)。

 

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved