您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)如何通過JSF 2實現(xiàn)可重用的Ajax化組件,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
我將向您介紹如何實現(xiàn)自動完成組件,它將使用Ajax管理其完成項列表。
JSF自動完成自定義組件
因谷歌搜索字段而聞名的自動完成字段(也稱作建議框)是許多Web應用程序的組合。它們也是Ajax的典型應用。自動完成字段隨帶了許多Ajax框架,比如Scriptaculous和JQuery,如圖1—AjaxDaddy的自動完成組件集成(參閱參考資料)—所示:
圖1.AjaxDaddy自動完成組件
本文將討論如何使用JSF來實現(xiàn)支持Ajax的自動完成字段。您將了解如何實現(xiàn)如圖2所示的自動完成字段,其中將顯示一個簡短的虛擬國家列表(選自Wikipedia的“虛擬國家列表”一文;請參閱參考資料):
圖2.自動完成字段
圖3和圖4顯示了運行中的自動完成字段。在圖3中,在字段中輸入Al之后,國家列表將縮減至使用這兩個字母開頭的名稱:
圖3.使用Al開頭的完成項目
同樣,圖4顯示了在字段中輸入Bar之后顯示的結(jié)果。列表僅顯示以Bar開頭的國家名。
圖4.以Bar開頭的完成項目
使用自動完成組件
復合組件:基礎如果您不熟悉如何實現(xiàn)JSF2復合組件,那么您可以從“JSF2簡介,第2部分:模板及復合組件”這篇文章中了解基本知識。
.Locations自動完成字段是一個JSF復合組件,并應用于facelet,如清單1所示:
清單1.facelet
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:util="http://java.sun.com/jsf/composite/util"> <h:head> <title>#{msgs.autoCompleteWindowTitle}</title> </h:head> <h:body> <div style="padding: 20px;"> <h:form> <h:panelGrid columns="2"> #{msgs.locationsPrompt} <util:autoComplete value="#{user.country}" completionItems="#{autoComplete.countries}" /> </h:panelGrid> </h:form> </div> </h:body> </html>
清單1中的facelet通過聲明適當?shù)拿Q空間—util—以及借助組件的相關(guān)標記(<util:autoComplete>)來使用autoComplete復合組件。
注意清單1中<util:autoComplete>標記的兩個屬性:
•value是名稱為user的托管bean的國家屬性。
•completionItems是字段的完成項目的初始集。
User類是一個簡單的托管bean,專為本例而設計。其代碼如清單2所示:
清單2.User類
package com.corejsf; import java.io.Serializable; import javax.inject.Named; import javax.enterprise.context.SessionScoped; @Named() @SessionScoped public class User implements Serializable { private String country; public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
請注意@Named注釋,它與@SessionScoped一起實例化了一個名稱為user的托管bean,并在JSF第一次在facelet中遇到#{user.country}時將它置于session作用域中。此應用程序中唯一的#{user.country}引用發(fā)生在清單1中,其中,我將user托管bean的country屬性指定為<util:autoComplete>組件的值。
清單3顯示了AutoComplete類,該類定義了countries屬性,即自動完成組件的完成項目列表:
清單3.完成項目
package com.corejsf; import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; import javax.inject.Named; @Named @ApplicationScoped public class AutoComplete implements Serializable { public String[] getLocations() { return new String[] { "Abari", "Absurdsvanj", "Adjikistan", "Afromacoland", "Agrabah", "Agaria", "Aijina", "Ajir", "Al-Alemand", "Al Amarja", "Alaine", "Albenistan", "Aldestan", "Al Hari", "Alpine Emirates", "Altruria", "Allied States of America", "BabaKiueria", "Babalstan", "Babar's Kingdom","Backhairistan", "Bacteria", "Bahar", "Bahavia", "Bahkan", "Bakaslavia", "Balamkadar", "Baki", "Balinderry", "Balochistan", "Baltish", "Baltonia", "Bataniland, Republic of", "Bayview", "Banania, Republica de", "Bandrika", "Bangalia", "Bangstoff", "Bapetikosweti", "Baracq", "Baraza", "Barataria", "Barclay Islands", "Barringtonia", "Bay View", "Basenji", }; } }
自動完成組件的使用方法已經(jīng)介紹完畢。現(xiàn)在,您將了解它的工作原理。
自動完成組件的工作原理
自動完成組件是一個JSF2復合組件,因此,與大多數(shù)復合組件相同,它是在XHTML文件中實現(xiàn)的。組件包括一個文本輸入和一個列表框,以及一些JavaScript代碼。最開始,列表框style是display:none,其作用是讓列表框不可見。
自動完成組件響應三個事件:
◆文本輸入中的keyup事件
◆文本輸入中的blur(失焦)事件
◆列表框中的change(選擇)事件
當用戶在文本輸入中鍵入內(nèi)容時,自動完成組件會調(diào)用每個keyup事件的JavaScript函數(shù)。該函數(shù)結(jié)合鍵盤輸入事件,以不大于350ms的間隔定期調(diào)用Ajax。因此,在響應文本輸入中的keyup事件時,自動完成組件會以不大于350ms的間隔定期向服務器發(fā)起Ajax調(diào)用。(其作用是防止快速輸入時的大量Ajax調(diào)用將服務器淹沒。在實踐中,結(jié)合事件的頻率可能會稍高,但這足以演示如何在JavaScript中結(jié)合事件,同時這是一個非常實用的工具。)
當用戶從列表框中選擇項目時,自動完成組件會向服務器發(fā)起另一個Ajax調(diào)用。
文本輸入和列表框都附加了監(jiān)聽程序,它們在Ajax調(diào)用期間完成與服務器相關(guān)的大部分有意義的工作。在響應keyup事件時,文本輸入的監(jiān)聽程序會更新列表框的完成項目。在響應列表框的選擇事件時,列表框的監(jiān)聽程序會將列表框的選中項目復制到文本輸入中,并隱藏列表框。
現(xiàn)在,您已經(jīng)了解了自動完成組件的工作原理。接下來,我們來看看它的具體實現(xiàn)。
實現(xiàn)自動完成組件
自動完成組件實現(xiàn)包括以下工件:
◆一個復合組件
◆一系列JavaScript函數(shù)
◆一個用于更新完成項目的值變更監(jiān)聽程序
我將從清單4開始復合組件:
清單4.autoComplete組件
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:composite="http://java.sun.com/jsf/composite"> <!-- INTERFACE --> <composite:interface> <composite:attribute name="value" required="true"/> <composite:attribute name="completionItems" required="true"/> </composite:interface> <!-- IMPLEMENATION --> <composite:implementation> <div id="#{cc.clientId}"> <h:outputScript library="javascript" name="prototype-1.6.0.2.js" target="head"/> <h:outputScript library="javascript" name="autoComplete.js" target="head"/> <h:inputText id="input" value="#{cc.attrs.value}" onkeyup="com.corejsf.updateCompletionItems(this, event)" onblur="com.corejsf.inputLostFocus(this)" valueChangeListener="#{autocompleteListener.valueChanged}"/> <h:selectOneListbox id="listbox" style="display: none" valueChangeListener="#{autocompleteListener.completionItemSelected}"> <f:selectItems value="#{cc.attrs.completionItems}"/> <f:ajax render="input"/> </h:selectOneListbox> <div> </composite:implementation> </ui:composition>
清單4的實現(xiàn)部分完成了三項任務。首先,該組件發(fā)起Ajax調(diào)用以響應文本輸入中的keyup事件,并通過分配給文本輸入中的keyup和blur事件的JavaScript函數(shù)在文本輸入失焦時隱藏列表框。
其實,該組件通過JSF2的<f:ajax>標記來發(fā)起Ajax調(diào)用來響應列表框中的change事件。當用戶從列表框中進行選擇時,JSF會向服務器發(fā)起一個Ajax調(diào)用,并在Ajax調(diào)用返回時更新文本輸入的值。
在<div>中封裝復合組件清單4中的復合組件通過復合組件的客戶機標識符將其實現(xiàn)封裝在<div>中。這樣,其他組件便可通過其客戶機ID來引用自動完成組件。舉例來說,另一個組件可能會希望在Ajax調(diào)用期間執(zhí)行或呈現(xiàn)一個或多個自動完成組件。
.第三,文本輸入和列表框都附加了相應的值變更監(jiān)聽程序,因此當JSF發(fā)起Ajax調(diào)用來響應用戶在文本輸入中的鍵入操作時,JSF會調(diào)用服務器上的文本輸入的值變更監(jiān)聽程序。當用戶從列表框中選擇項目時,JSF會向服務器發(fā)起一個Ajax調(diào)用并調(diào)用列表框的值變更監(jiān)聽程序。
清單5顯示了自動完成組件所使用的JavaScript:
清單5.JavaScript
if (!com) var com = {} if (!com.corejsf) { var focusLostTimeout com.corejsf = { errorHandler : function(data) { alert("Error occurred during Ajax call: " + data.description) }, updateCompletionItems : function(input, event) { var keystrokeTimeout jsf.ajax.addOnError(com.corejsf.errorHandler) var ajaxRequest = function() { jsf.ajax.request(input, event, { render: com.corejsf.getListboxId(input), x: Element.cumulativeOffset(input)[0], y: Element.cumulativeOffset(input)[1] + Element.getHeight(input) }) } window.clearTimeout(keystrokeTimeout) keystrokeTimeout = window.setTimeout(ajaxRequest, 350) }, inputLostFocus : function(input) { var hideListbox = function() { Element.hide(com.corejsf.getListboxId(input)) } focusLostTimeout = window.setTimeout(hideListbox, 200) }, getListboxId : function(input) { var clientId = new String(input.name) var lastIndex = clientId.lastIndexOf(':') return clientId.substring(0, lastIndex) + ':listbox' } } }
清單5中的JavaScript包括三個函數(shù),我把它們放置在com.corejsf名稱空間的內(nèi)部。我實現(xiàn)了名稱空間(從技術(shù)上說是一個JavaScript字面對象),以防止其他人有意或無意修改我的三個函數(shù)。
如果這些函數(shù)未包含在com.corejsf中,則其他人可以實現(xiàn)自己的updateCompletionItems函數(shù),從而將我的實現(xiàn)替換成它們。一些JavaScript庫可以實現(xiàn)一個updateCompletionItems函數(shù),但最理想的情況是任何人都不用設計com.corejsf.updateCompletionItems。(相反,拋棄com,并使用corejsf.updateCompletionItems可能已經(jīng)足夠,但有時會難以控制。)
因此,這些函數(shù)做了些什么?updateCompletionItems()函數(shù)向服務器發(fā)起Ajax請求—通過調(diào)用JSF的jsf.ajax.request()函數(shù)—要求JSF僅在Ajax調(diào)用返回時呈現(xiàn)列表框組件。updateCompletionItems()函數(shù)還傳遞了兩個額外的參數(shù)到jsf.ajax.request()中:列表框左上角的x和y坐標。jsf.ajax.request()函數(shù)會將這些函數(shù)參數(shù)轉(zhuǎn)換為通過Ajax調(diào)用發(fā)送的請求參數(shù)。
JSF會在文本輸入失焦時調(diào)用inputLostFocus()函數(shù)。該函數(shù)的作用是使用Prototype的Element對象來隱藏列表框。
updateCompletionItems()和inputLostFocus()將它們的功能存儲在一個函數(shù)中。然后,它們安排自己的函數(shù)分別在350ms和200ms時執(zhí)行。換句話說,每個函數(shù)都有各自的任務,但它會讓任務延時350ms或200ms。文本輸入會在keyup事件后延時,因此,updateCompletionItems()方法會最多每隔350ms發(fā)送一個Ajax請求。其思想是,如果用戶輸入速度極快,則不會讓Ajax調(diào)用淹沒服務器。
inputLostFocus函數(shù)會在文本輸入失焦時調(diào)用,并延時其任務200ms。這種延時是必要的,因為該值會在Ajax調(diào)用返回時復制到列表框之外,并且列表框必須為可見才能確保它正常運行。
最后,請注意getListBoxId()函數(shù)。這個幫助器函數(shù)會從文本輸入的客戶機標識符中獲取列表框的客戶機標識符。該函數(shù)可以完成此任務,因為它將與清單4中的autoComplete組件相結(jié)合。autoComplete組件將input和listbox分別指定為文本框和列表框的組件標識符,因此getListBoxId()函數(shù)會刪除input并附加listbox,以便獲取文本輸入的客戶機標識符。
清單6顯示了監(jiān)聽程序的最終實現(xiàn):
清單6.監(jiān)聽程序
package com.corejsf; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.enterprise.context.SessionScoped; import javax.faces.component.UIInput; import javax.faces.component.UISelectItems; import javax.faces.component.UISelectOne; import javax.faces.context.FacesContext; import javax.faces.event.ValueChangeEvent; import javax.inject.Named; @Named @SessionScoped public class AutocompleteListener implements Serializable { private static String COMPLETION_ITEMS_ATTR = "corejsf.completionItems"; public void valueChanged(ValueChangeEvent e) { UIInput input = (UIInput)e.getSource(); UISelectOne listbox = (UISelectOne)input.findComponent("listbox"); if (listbox != null) { UISelectItems items = (UISelectItems)listbox.getChildren().get(0); Map<String, Object> attrs = listbox.getAttributes(); List<String> newItems = getNewItems((String)input.getValue(), getCompletionItems(listbox, items, attrs)); items.setValue(newItems.toArray()); setListboxStyle(newItems.size(), attrs); } } public void completionItemSelected(ValueChangeEvent e) { UISelectOne listbox = (UISelectOne)e.getSource(); UIInput input = (UIInput)listbox.findComponent("input"); if(input != null) { input.setValue(listbox.getValue()); } Map<String, Object> attrs = listbox.getAttributes(); attrs.put("style", "display: none"); } private List<String> getNewItems(String inputValue, String[] completionItems) { List<String> newnewItems = new ArrayList<String>(); for (String item : completionItems) { String s = item.substring(0, inputValue.length()); if (s.equalsIgnoreCase(inputValue)) newItems.add(item); } return newItems; } private void setListboxStyle(int rows, Map<String, Object> attrs) { if (rows > 0) { Map<String, String> reqParams = FacesContext.getCurrentInstance() .getExternalContext().getRequestParameterMap(); attrs.put("style", "display: inline; position: absolute; left: " + reqParams.get("x") + "px;" + " top: " + reqParams.get("y") + "px"); attrs.put("size", rows == 1 ? 2 : rows); } else attrs.put("style", "display: none;"); } private String[] getCompletionItems(UISelectOne listbox, UISelectItems items, Map<String, Object> attrs) { Strings] completionItems = (String[])attrs.get(COMPLETION_ITEMS_ATTR); if (completionItems == null) { completionItems = (String[])items.getValue(); attrs.put(COMPLETION_ITEMS_ATTR, completionItems); } return completionItems; } }
JSF在Ajax調(diào)用期間調(diào)用監(jiān)聽程序的valueChanged()方法來響應文本輸入中的keyup事件。該方法會創(chuàng)建一組新的完成項目,然后將列表框的項目設置為這個新的項目集。該方法還會設置列表框的樣式屬性,以確定Ajax調(diào)用返回時是否顯示列表框。
清單6中的setListboxStyle()方法將使用x和y請求我在發(fā)起清單5中的Ajax調(diào)用時指定的參數(shù)值。
JSF會在Ajax調(diào)用期間調(diào)用監(jiān)聽程序的其他公共方法completionItemSelected(),以響應列表框中的選擇事件。該方法會將列表框的值復制到文本輸入中,并隱藏列表框。
請注意,valueChanged()方法還會將原始完成項目存儲在列表框的某個屬性中。由于每個autoComplete組件都維護自己的完成項目列表,因此多個autoComplete組件可以在相同頁面中和諧共存,而不會影響彼此的完成項目。
使用GlassFish和Eclipse運行示例
本系列中的代碼適合在JEE6容器中運行,比如GlassFish或Resin。您可以通過調(diào)整讓它們適應servlet容器,但這并非理想方案。因此,我的目標是側(cè)重于充分發(fā)揮JSF2和JEE6的潛力,而不是配置問題。我仍然堅持使用GlassFishv3。
在本文的其余部分,我將向您展示如何使用GlassFishv3和Eclipse來運行本文的示例代碼。此處的說明還適用于本系列其他文章的代碼。(我將使用Eclipse3.4.1,因此最好是使用與之相近的版本。)
圖5展示了本文代碼的目錄結(jié)構(gòu)。其中的autoComplete目錄包含應用程序和一個空的Eclipse工作空間目錄。
圖5.本文下載部分的源代碼
現(xiàn)在,您已經(jīng)下載了代碼,接下來就可以運行它了。首先,您需要GlassFishEclipse插件,可從https://glassfishplugins.dev.java.net下載它,如圖6所示:
圖6.GlassFishEclipse插件
請依照插件的安裝說明操作。
要安裝本文的代碼,請在Eclipse中創(chuàng)建一個DynamicWeb項目。為此,可以通過File>New菜單來實現(xiàn):如果未看到DynamicWebProject,那么請選擇Other,并在接下來的對話框中打開Web文件夾并選擇DynamicWebProject,如圖7所示:
圖7.創(chuàng)建一個DynamicWeb項目
下一步是配置項目。在NewDynamicWebProject向?qū)У牡谝粋€頁面中做出以下選擇,如圖8所示:
1.在Projectcontents下,保留Usedefault框為未選中狀態(tài)。在Directory字段中,輸入(或瀏覽到)示例代碼的autoComplete目錄。
2.對于TargetRuntime,請選擇GlassFishv3JavaEE6。
3.對于DynamicWebModuleversion,請輸入2.5。
4.對于Configuration,請選擇DefaultConfigurationforGlassFishv3JavaEE6。
5.在EARMembership下,保留AddprojecttoanEAR框為未選中狀態(tài),并在EARProjectName:字段中輸入autoCompleteEAR。
圖8.配置應用程序,步驟1
單擊Next,然后輸入如圖9所示的值:
1.對于ContextRoot:,輸入autoComplete。
2.對于ContentDirectory:,輸入web。
3.對于JavaSourceDirectory:,輸入src/java。保留Generatedeploymentdescriptor框為未選中狀態(tài)。
圖9.配置應用程序,步驟2
現(xiàn)在,您應該已經(jīng)建立了一個autoComplete項目,它將顯示在Eclipse的ProjectExplorer視圖中,如圖10所示:
圖10.autoComplete項目
現(xiàn)在,選擇項目,右鍵單擊它并選擇RunonServer,如圖11所示:
圖11.使用Eclipse在服務器上運行
從RunOnServer對話框的服務器列表中選擇GlassFishv3JavaEE6,如圖12所示:
圖12.選擇GlassFish
單擊Finish。Eclipse應該會相繼啟動GlassFish和autoComplete應用程序,如圖13所示:
13.在Eclipse中運行
借助JSF2,開發(fā)人員可以輕松地創(chuàng)建功能強大、支持Ajax的自定義組件。您不需要在XML中實現(xiàn)基于Java的組件或呈現(xiàn)器,或者集成第三方JavaScript來發(fā)起Ajax調(diào)用。借助JSF2,您只需要使用幾乎與任何JSF2facelet視圖相同的標記來創(chuàng)建一個復合組件,并根據(jù)需求添加一些JavaScript或Java代碼,以及voilà—您將實現(xiàn)一個奇妙自定義組件,為應用程序用戶提供極為方便的數(shù)據(jù)輸入功能。
以上就是如何通過JSF 2實現(xiàn)可重用的Ajax化組件,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。