JSF理解組件和客戶(hù)端標(biāo)識(shí)符
理解組件和客戶(hù)端標(biāo)識(shí)符
我們接觸了客戶(hù)端標(biāo)識(shí)符的概念,現(xiàn)在來(lái)看看它與在JSP中分配給組件的標(biāo)識(shí)符有何不同。
我們說(shuō)過(guò),UI 組件跨越兩個(gè)世界:在服務(wù)器端,它被表示為組件樹(shù)中的一個(gè)對(duì)象;在客戶(hù)端,它可以有多種表現(xiàn)形式。服務(wù)器是由一個(gè)Java虛擬機(jī)以及servlet、 JSF、應(yīng)用代碼和其他支持庫(kù)組成??蛻?hù)端則通常是能夠顯示諸如HTML之類(lèi)的標(biāo)記的瀏覽器。瀏覽器則是屬于客戶(hù)端腳本語(yǔ)言(如JavaScript或者 VBScript)、層疊樣式表(CSS)之類(lèi)的樣式機(jī)制以及像錨和超鏈接之類(lèi)的導(dǎo)航方案的世界。
在兩個(gè)世界中都需要有找到特定組件的特定的方式。在服務(wù)器端,每個(gè)組件都可以通過(guò)組件標(biāo)識(shí)符找到。如果你為某個(gè)組件指定了標(biāo)識(shí)符,便可以在 Java 代碼中訪(fǎng)問(wèn)該組件。在客戶(hù)端,每個(gè)組件都可以通過(guò)其客戶(hù)端標(biāo)識(shí)符找到,而該標(biāo)識(shí)符則衍生自組件標(biāo)識(shí)符??蛻?hù)端標(biāo)識(shí)符是允許你通過(guò)JavaScript、 CSS和其他類(lèi)似技術(shù)來(lái)操作組件的表現(xiàn)。
客戶(hù)端標(biāo)識(shí)符也在服務(wù)器和客戶(hù)端之間架了一座橋。當(dāng)用戶(hù)提交表單時(shí),客戶(hù)端標(biāo)識(shí)符也隨表示用戶(hù)對(duì)該組件所執(zhí)行的動(dòng)作的數(shù)據(jù)一起發(fā)送給服務(wù)器,然后被用來(lái)將用戶(hù)數(shù)據(jù)映射到服務(wù)器上的組件實(shí)例,以便修改其值,產(chǎn)生事件,以及進(jìn)行其他一些操作。
這似乎有些含混,所以我們來(lái)看一個(gè)例子。展示了運(yùn)行于服務(wù)器端的組件、它們?cè)诳蛻?hù)端的表示以及使用標(biāo)識(shí)符的技術(shù)種類(lèi)之間的關(guān)系。下面的 JSP代碼定義了圖中所示的組件——一個(gè) HtmlOutputText組件和一個(gè)具有兩個(gè)HtmlInputText子組件的HtmlForm組件:
服務(wù)器中的組件實(shí)例是通過(guò)組件標(biāo)識(shí)符來(lái)引用的。而在客戶(hù)端它們則是通過(guò)客戶(hù)端標(biāo)識(shí)符來(lái)引用的。有時(shí)候兩者相同
首先,需要指出的是,這里的id屬性是組件標(biāo)識(shí)符。另外,你將注意到***個(gè)<h:inputText>元素并沒(méi)有特定的標(biāo)識(shí)符。這是因?yàn)闃?biāo)識(shí)符是可選的——如果沒(méi)有指定一個(gè),JSF將生成一個(gè)。
組件標(biāo)識(shí)符必須由字母或下劃線(xiàn)開(kāi)始,并且由字母、數(shù)字、連字符和下劃線(xiàn)組成。它們也應(yīng)該簡(jiǎn)短一些,以便使返回給客戶(hù)端的響應(yīng)最小化。
現(xiàn)在,來(lái)看看對(duì)應(yīng)的HTML輸出:
HTML <span>元素是由HtmlOutputText組件產(chǎn)生的。其客戶(hù)端標(biāo)識(shí)符為outputText,與我們?cè)贘SP為其定義的組件標(biāo)識(shí)符一樣。<form> 元素是HtmlForm 組件的輸出,其客戶(hù)端標(biāo)識(shí)符也與組件標(biāo)識(shí)符相同。但是其所有子組件的客戶(hù)端標(biāo)識(shí)符卻要以HtmlForm 組件的客戶(hù)端標(biāo)識(shí)符myForm開(kāi)始。因?yàn)槲覀儧](méi)有為第二個(gè)HtmlInputText組件指定組件標(biāo)識(shí)符,JSF自動(dòng)為其分配了一個(gè)標(biāo)識(shí)符_id0(輸入控件通常將id和name屬性一起輸出為客戶(hù)端標(biāo)識(shí)符)。
HtmlForm 組件的子組件的客戶(hù)端標(biāo)識(shí)符以HtmlForm的客戶(hù)端標(biāo)識(shí)符開(kāi)始,因?yàn)镠tmlForm組件是個(gè)命名容器。命名容器是下一節(jié)的內(nèi)容。
注解 組件標(biāo)識(shí)符通常是可選的,但是如果需要用一個(gè)組件引用另一個(gè)組件,或者通過(guò)已知的標(biāo)識(shí)符在客戶(hù)端或服務(wù)器端引用組件時(shí),則是必要的。如果沒(méi)有為其指定,JSF 將在服務(wù)器中自動(dòng)產(chǎn)生一個(gè),但是取決于具體的組件,你可能不能在呈現(xiàn)好的標(biāo)記中看到客戶(hù)端標(biāo)識(shí)符。而如果指定了一個(gè)組件標(biāo)志符,則盡可能地保持其簡(jiǎn)短,以便減小JSF響應(yīng)的大小。
命名容器
命名容器是個(gè)組件,它的所有兒子都有***的組件標(biāo)識(shí)符。所以不能在一個(gè)命名容器中存在兩個(gè)具有相同組件標(biāo)識(shí)符的組件。視圖的根節(jié)點(diǎn)(UIViewRoot),即某個(gè)給定頁(yè)面中所有組件的父親,不是命名容器,但是的確需要使同一個(gè)視圖中的頂層組件具有不同的標(biāo)識(shí)符。
HtmlForm就是命名容器,所以位于同一個(gè) HtmlForm 中的組件不能有相同的組件標(biāo)識(shí)符。這是有意義的,因?yàn)槿绻哂袃蓚€(gè)名稱(chēng)都為foo的組件并且類(lèi)型也相同,你將不能區(qū)分它們。另一個(gè)標(biāo)準(zhǔn)的命名容器是 HtmlDataTable。某些第三方組件(或者你自己編寫(xiě)的組件),也可以是命名容器。
如果在控件層次體系中有不只一個(gè)命名容器,客戶(hù)端標(biāo)識(shí)符可以不同于組件標(biāo)識(shí)符。這是因?yàn)榭蛻?hù)端標(biāo)識(shí)符對(duì)整個(gè)頁(yè)面必須保證***,而不管其中有多少命名組件。它們必須是***的,因?yàn)榭蛻?hù)并不知道命名容器——它僅僅提交回表單數(shù)據(jù)給JSF 應(yīng)用。應(yīng)用必須能將數(shù)據(jù)映射到具體的組件,即必須能夠區(qū)分針對(duì)某個(gè)組件的數(shù)據(jù)。
為了演示,來(lái)看看同一個(gè)視圖中的兩個(gè)HtmlForm組件。它們都包含一個(gè)具有相同組件標(biāo)識(shí)符的HtmlInputText子組件:
這兩個(gè)聲明,除了HtmlForm 組件的標(biāo)識(shí)符不同外,其余都是一樣的。下面是與其對(duì)應(yīng)的HTML輸出:
UIViewRoot不產(chǎn)生任何輸出;它僅是標(biāo)明組件樹(shù)的開(kāi)始。然而重要的是,即便兩個(gè)HtmlTextInput組件具有相同的組件標(biāo)識(shí)符 inputText,但其客戶(hù)端標(biāo)識(shí)符卻是不同的。因?yàn)?**個(gè)表單的客戶(hù)端標(biāo)識(shí)符是firstForm,其子控件的客戶(hù)端標(biāo)識(shí)符則為 firstForm:inputText。而后一個(gè)表單的客戶(hù)端標(biāo)識(shí)符是 secondForm,其子組件的客戶(hù)端標(biāo)識(shí)符為secondForm:inputText。如你所見(jiàn),客戶(hù)端標(biāo)識(shí)符等于命名容器的客戶(hù)端標(biāo)識(shí)符加上冒號(hào)再加上其自身的組件標(biāo)識(shí)符。
因?yàn)榭蛻?hù)端標(biāo)識(shí)符的默認(rèn)分隔符為":",這在CSS樣式,試圖使用客戶(hù)端標(biāo)識(shí)符應(yīng)用樣式到某個(gè)組件時(shí),可能會(huì)出現(xiàn)問(wèn)題。解決方法是對(duì)組件應(yīng)用CSS 類(lèi)樣式(基于此目的,所有的標(biāo)準(zhǔn)HTML 組件都有一個(gè)styleClass屬性)。
你可能想知道命名容器將如何影響JSF開(kāi)發(fā)人員的日常開(kāi)發(fā)工作。知道哪個(gè)組件是命名容器,將有助于你了解一個(gè)組件的客戶(hù)端標(biāo)識(shí)符。并且,如果你認(rèn)識(shí)客戶(hù)端標(biāo)識(shí)符,便可以在客戶(hù)端引用它,并且在服務(wù)器中為其解碼,這些都可以在后面的內(nèi)容中看到。
引用標(biāo)識(shí)符
好了,現(xiàn)在已經(jīng)知道UI組件在客戶(hù)端和服務(wù)器端都有標(biāo)識(shí)符,并且知道如果涉及命名容器這些標(biāo)識(shí)符可能不同。現(xiàn)在我們來(lái)看看如何使用這些標(biāo)識(shí)符在客戶(hù)端和服務(wù)器引用組件。
1.客戶(hù)端引用
如前所述,客戶(hù)端技術(shù)可以通過(guò)客戶(hù)端標(biāo)識(shí)符引用組件。請(qǐng)看下面的JSP 代碼:
這里有個(gè)HtmlForm 組件,而它又分別有兩個(gè)HtmlOutputLabel 和 HtmlInputText 子組件。HtmlOutputLabel組件有一個(gè)HtmlOutput子組件。這樣產(chǎn)生了下面的HTML代碼:
label元素的onmouseout 和 onmouseover屬性中的JavaScript代碼,通過(guò)input字段的name 屬性(clientForm:myTextBox)引用了它,該名稱(chēng)也是HtmlInputText 組件的客戶(hù)端標(biāo)識(shí)符。用戶(hù)的鼠標(biāo)移到label上,文本框的值將變?yōu)?84"。用戶(hù)移開(kāi)鼠標(biāo),它將變?yōu)榭?。這并不是什么很有用的功能,但從這知道,你可以在JavaScript 中通過(guò)其客戶(hù)端標(biāo)識(shí)符訪(fǎng)問(wèn)文本字段。
針對(duì)HTML 瀏覽器,組件的客戶(hù)端標(biāo)識(shí)符映射至對(duì)應(yīng)的HTML元素的name或者id屬性。這意味著也可以在CSS中使用它,或者將其作為錨引用等(關(guān)于HTML 的快速參考,請(qǐng)?jiān)L問(wèn)W3School的站點(diǎn)[W3Schools])。
然而,請(qǐng)記住JSF 并不限于是HTML應(yīng)用,可以是不同類(lèi)型的瀏覽器、桌面客戶(hù)、applet或者干脆完全不同的其他東西。不管使用何種技術(shù),運(yùn)行于客戶(hù)端的代碼都必須考慮客戶(hù)端標(biāo)識(shí)符,特別是在和服務(wù)器通信時(shí)。
2.服務(wù)器端引用
當(dāng)你在服務(wù)器上和JSF組件交互時(shí),編寫(xiě)的代碼通常位于后臺(tái)bean的事件監(jiān)聽(tīng)器方法或者事件監(jiān)聽(tīng)器類(lèi)中。然而,它可以位于任何位置,只要具有對(duì)組件實(shí)例的引用。這是因?yàn)榛窘M件類(lèi)—— UIComponent——具有一個(gè)快捷的findComponent方法。該方法使用類(lèi)似于客戶(hù)端標(biāo)識(shí)符的特殊表達(dá)式來(lái)查找組件。
例如,假定在JSP中定義了如下的表單:
這樣就定義了一個(gè)messageForm的 HtmlForm組件,以及一個(gè)outputMessage的HtmlOutputText子組件以及沒(méi)有指定標(biāo)識(shí)符的 HtmlCommandButton子組件。HtmlCommandButton 引用了testForm.sendMessage 事件監(jiān)聽(tīng)器方法,該方法是:
這里,我們首先獲取視圖的根組件,然后使用 HtmlOutputText 組件的客戶(hù)端標(biāo)識(shí)符調(diào)用findComponent方法。接下來(lái)修改了它的顏色(使用CSS樣式)和值。用戶(hù)點(diǎn)擊按鈕時(shí),此方法被調(diào)用,并且在頁(yè)面重新顯示時(shí),HtmlOutputText控件將以藍(lán)色顯示“Who’s the Mann?”(后臺(tái)bean也可以直接引用組件,特別是由IDE產(chǎn)生時(shí),所以這種查找并不是很必要)。關(guān)于findComponent方法的詳細(xì)信息,參考第11章。在為了將請(qǐng)求參數(shù)映射到組件值而編寫(xiě)組件和呈現(xiàn)器時(shí),你也需要使用客戶(hù)端標(biāo)識(shí)符。
現(xiàn)在,已經(jīng)知道關(guān)于組件和客戶(hù)端標(biāo)識(shí)符的所有內(nèi)容,接下來(lái)則看看JSF的另一個(gè)基本部分,在日常工作中會(huì)經(jīng)常遇到:JSF表達(dá)式語(yǔ)言。
【編輯推薦】