<checkbox id="cbx_1" value="N" labelonleft="true"
label="Show Details:" onValue="Y" offValue="N"/>
另外,我們的控件將支持可取消的事件onItemChanging和通知事件onItemChanged。
定義定制標簽
從結構上講,一個定制標簽是一個具有一個HTC擴展名的文件-它在<PUBLIC:COMPONENT>和</PUBLIC:COMPONENT>標志之間對它的屬性,方法和事件加以描述。
為了定義一個定制CHECKBOX標簽,我們創建一個如下列代碼片斷中的文件checkbox.htc-其中,第一行負責設置該組件的標簽名:
<PUBLIC:COMPONENT NAME="cbx" tagName="CHECKBOX">
<PROPERTY NAME="value" GET="getValue" PUT="putValue" />
//我們把組件的所有另外的屬性放在這裡
<METHOD NAME="show" />
//我們把組件的所有另外的方法放在這裡
<EVENT NAME="onItemChanging" ID="onItemChanging"/>
//我們把組件將向應用程序激活的所有另外的事件放在這裡
<ATTACH EVENT="oncontentready" HANDLER="constructor" />
//我們把組件自己處理的另外的事件放在這裡
<SCRIPT>
//我們把所有的方法,屬性getters和setters和事件處理器放在這裡
</SCRIPT>
</PUBLIC:COMPONENT>
使用定制標簽
盡管HTC文件的內容比較重要,但是這與其文件名是什麼無關。值得注意的是,指向該HTC文件的URL需要被使用IMPORT指令指定-這必須在相應的定制標簽第一次出現之前(在頁面上)完成。下面是最簡單的可能的頁面使用一個定制的復選框可能看上去的樣子-假定該頁面和HTC文件處理同一個文件夾下:
<Html XMLns:myns>
<?IMPORT namespace="myns" implementation="checkbox.htc" >
<BODY>
<myns:checkbox id='cbx_1' label='Hello'/>
</BODY>
</Html>
請注意,定制CHECKBOX是怎樣在打開的Html標簽中被映射到一個非缺省的命名空間"myns"的。這個IMPORT指令實現把HTC同步加載到浏覽器的內存並且還指示浏覽器怎樣為適當的命名空間實現名稱確定的(HTC到命名空間的關聯可能是多對一的)。
定制標簽的構造器
最好的初始化HTC的方法是,一旦它被裝載就處理oncontentready事件。因此,我們可以定義處理器函數-為了概念清晰起見,我們稱之為構造器:
<ATTACH EVENT="oncontentready" HANDLER="constructor" />
constructor()的邏輯是簡單的:根據屬性labelonleft的值(見下面的屬性定義)按順序連接一個常規HTML復選框和Html標簽:
function constructor() {
//我們將把一個Html復選框和標簽添加到元素體
//詳細情形見列表2
}
定義定制標簽屬性
為了定義屬性labelonleft,我們又在<PUBLIC:COMPONENT>部分加上一行:
<PROPERTY NAME="labelonleft" VALUE="true"/>
請注意,這個屬性並沒有包含getter和/或setter方法。屬性onValue和offValue不僅提供了從復選框狀態到一個商業值域的映射而且不需要getters和setters:
<PROPERTY NAME="onValue" VALUE="true"/>
<PROPERTY NAME="offValue" VALUE="false" />
然而,屬性checked是用兩個getter和setter定義的:
<PROPERTY NAME="checked" GET="getChecked" PUT="putChecked" />
因此,我們在<SCRIPT>部分建立了上面兩個方法的定義。正如你所見,setter putChecked()-將在每次復選框狀態改變時激發-把value屬性設置為下面兩個變體之一:onValue或OffValue。請注意,putChecked()將不僅可由在復選框-宿主頁面中的腳本觸發,而且也能通過在checkbox.htc中的相應的任何賦值操作觸發。
var _value;
function putChecked( newValue ) {
value = (newValue?onValue:offValue);
}
function getChecked(){
return ( _value == onValue);
}
六、為定制標簽定義事件
讓我們看一下onItemChanging和onItemChanged事件的定義以及這些事件是怎樣在value屬性的setter內部被激發和處理的(見所附源碼中的列表2)。方法putValue()有幾個讓人感興趣的地方。首先,在分析CHECKBOX標簽期間,可以調用這個方法-只要指定這個Htmlvalue屬性。正解釋了為什麼我們為非構造對象建立一個單獨的邏輯分支-為把構造過程與一個對用戶擊鍵的反應區別開來。其次,在此我們展示了定制事件onItemChanging的創建和處理-它允許應用程序取消行為。請注意,通過這種方式,無論是擊鍵還是通過編程方式實現賦值都能達到取消的目的。
事件取消
為了取消事件,一個應用程序應該攔截該事件並且把event.returnValue設置為false。下面的代碼片斷展示了應用程序是怎樣實現取消事件過程的:
cbx_1::onItemChanging() {
. . . . .
if (canNotBeAllowed) {
event.returnValue=false;
. . . . .
}
如果事件沒被取消,putValue()把內部的普通Html復選框的checked屬性設置為每個相應的當前值-如果它等於onValue,這個內部復選框將被選中;如果它等於offValue(不存在第三種選擇),復選框不被選中(完整的列表見本文所附源碼中的列表2)。
復選框的Html內幕
我們控件的繪制是通過助理函數addLabel()和addCheckBox()來實現的並且從一個constructor()內部調用。這些函數把HTML注入進元素的innerHtml。這種注入式Html的一種簡化形式如下所示:
<LABEL for=cb_{uniqueID}>Show Details:</LABEL>
<INPUT id=cb_{uniqueID} type=checkbox />
在此,uniqueID是一個由IE所生成的唯一的(在一個頁面內)字符串-它用來識別HTC的實例。
七、 再封裝
在我們的CHECKBOX中有一個缺點。按照我們建立它的方式,在constructor()期間被注入的Html將隸屬於宿主該HTC的頁面的DOM。而且,全局的JavaScript變量like_value屬於它們所在的文檔的全局范圍。這是危險的,因為我們偶然會遇到命名沖突的可能性:最明顯的情形是使用同一個組件的多個實例。另外這還會導致一個可能性-我們的控件可能會偶然地用相同的名稱參考其它對象,反之也如此。
為簡化起見,需要建立一種專門的機制來為對象授權啟動一個真正模塊化方法。幸好,HTC技術支持一種智能答案-vIEwLink。
把一個控件聲明為封裝的最容易的方法是把一個額外聲明放到打開和關閉的PUBLIC:COMPONENT標簽之間:
<PUBLIC:DEFAULTS vIEwLinkContent/>
該控件立即就變成封裝性的;而且它有自己的Html文檔樹-成為主文檔的原子組件。該對象的每個實例有它自己的實例值的集合並且只有公共方法和屬性能夠從外界代碼中加以存取。換句話說,該vIEwLink機制充分地啟動了復雜的Web應用程序的設計和實現-通過使用一種真正的OO的基於組件的方法。
特別地,我們可以簡化代碼-通過從內部復選框和Html標簽的定義中刪除uniqueID後綴,因為我們不再擔心命名沖突。因此,我們可以替換下面這一行:
eval( 'cb_'+uniqueID).checked = ( _value == onValue ); 用
cb.checked = ( _value == onValue );
並相應地改變addCheckbox()和addLabel()。
八、 結論
既然AJAX競賽剛剛開始,那麼就不存在什麼AJax標准並且沒有現成的你可以依賴以構建你的應用程序的可廣為接受的RAD工具。雖然軟件供應商們可能還需要較長一段時間來創建這種強健的開發平台,AJax熱心者已經開始著手准備-通過一些良好定義的API把可重用的代碼塊封裝為商業組件。
以這種方向導航,本文概括了AJax語言的OO"力量"-JavaScript。另外,還展示了一種可用的組件-授權策略-客戶端定制標簽技術。我們在僅描述IE特定的定制標簽的同時,還另外提供了一個可下載的實例-適於Mozilla浏覽器的可擴展的綁定實例。