本文示例源代碼或素材下載
XForms 與 XML 的緊密集成使查看基於 XML 的 API 產生的結果非常方便。同時,XForms 功能非常強大,使用它能創建根據變化條件做出相應操作的動態界面。本文通過實例探討了如何應用這些功能。在實例中,我們創建了一個動態搜索引擎客戶機,該客戶機提供了不同的選項並能根據搜索引擎的選擇顯示不同的數據。
簡介
本文演示了如何創建一個足夠靈活的接口,使用這個接口能夠查看若干個基於 XML 的 API 產生的結果。方法是實現一個搜索引擎客戶機供用戶在其中選擇搜索數據的引擎並自動接收合適的條目字段和數據。此外,表單加載了“後台中”的數據,也就是說,在頁面上顯示結果不需要完全重新加載。
本例中使用的 API 包括 Yahoo! 和 Teoma 搜索引擎,但文章傳達的思想適用於任何能返回 XML 的 API。這兩個搜索引擎都有一個基於 REST 的接口,也就是說提供給它們一個 URL 和所有參數,它們將返回帶有結果的 XML。
XForms 實現對內容的動態更改無需通過將 XForms 可視化表示(visual presentation)鏈接到 XML 數據結構來重新提交來自服務器的頁面。當 XML 數據結構發生變化時,可視化表示將被重新計算並作出相應變化而無需重新加載頁面。更改隱藏在表示下面的 XML 數據可以通過很多種方法實現。比如說,本例中的表單包含了一個下拉菜單,它列出了可用的搜索引擎。用戶只能填寫與活動引擎關聯的字段。基本引擎表單如圖 1 所示。
圖 1. 尚未進行任何搜索的搜索表單
先決條件
在開始創建表單之前,需要准備好如下軟件:
能顯示 XForms 的浏覽器,例如帶有 XForms 擴展的 Firefox(參閱 參考資料 中的下載鏈接)。
支持 PHP 的 Web 服務器。特定於本例,PHP 必須帶有 cURL 擴展才能訪問搜索引擎(參閱 參考資料 中的下載鏈接)。
一個 Yahoo 搜索的應用程序 ID。Teoma 不需要 ID。可從 Yahoo Web 站點獲得此 ID(參閱 參考資料)。
創建 XForms 數據結構
為支持本例的搜索引擎,表單使用了三個實例來實現數據結構。每個實例都有惟一的名稱,以便稍後檢索。其中兩個元素是空容器,用作搜索引擎返回的 XML 的加載點。另一個實例包含了搜索的輸入數據:Yahoo 搜索的應用程序 ID、查詢字符串(定義為 q)、兩個 Yahoo 特有的字段(site 和 results)。這些數據位於 box 實例中,如清單 1 所示。
清單 1. XML 數據結構,第 1 部分<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-strict.dtd">
<Html
XMLns="http://www.w3.org/1999/xHtml"
XMLns:y="urn:yahoo:srch"
XMLns:xforms="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/XML-events"
>
<head>
<title>Using XForms To Build A Dynamic Search Engine</title>
<xforms:model id="model_dynsrch">
<xforms:instance id="box">
<search XMLns="">
<engine>Yahoo</engine>
<appid>your_yahoo_app_id</appid>
<q>horse</q>
<site></site>
<results></results>
<submitTeoma/>
<submitYahoo/>
</search>
</xforms:instance>
<xforms:bind nodeset="instance('box')/site"
relevant="instance('box')/engine = 'Yahoo'" />
<xforms:bind nodeset="instance('box')/results"
relevant="instance('box')/engine = 'Yahoo'" />
<xforms:bind nodeset="instance('box')/submitYahoo"
relevant="instance('box')/engine = 'Yahoo'" />
<xforms:bind nodeset="instance('box')/submitTeoma"
relevant="instance('box')/engine = 'Teoma'" />
<xforms:instance id="yahoo">
<ResultSet XMLns="urn:yahoo:srch"/>
</xforms:instance>
<xforms:instance id="teoma">
<SEARCHRESULTS XMLns=""/>
</xforms:instance>
很顯然,這個 XHTML 的獨到之處應該就是在 Html 標記中使用了 xmlns:y="urn:yahoo:srch"。這與 Yahoo 搜索的數據實例有關。Yahoo 使用 xmlns="urn:yahoo:srch" 返回搜索數據。因為 Yahoo 只識別這個特定的名稱空間,所以我們在引用其數據時也必須使用該名稱空間;否則,將顯示為“不可見(invisible)”。後面我們將看到所有引用 Yahoo 搜索結果的 XForms 代碼都使用了在頭部指定的 y:。(記住,前綴是什麼無關緊要;真正起作用的是名稱空間。)此處的實例定義中,為搜索結果的 XML 數據節點了調用了名稱空間。
box 實例同時包含了基本信息(2)和站點特定的信息(appid、site 和 results)。我們將根據引擎元素的值控制站點特定信息的外觀。比方說,綁定元素指定了只有當引擎為“Yahoo”時,site 和 results 元素才會“關聯(relevant)”在一起。稍後,當我們創建真正的表單時,會發現盡管這些項都在頁面上,但是只有關聯項才會在頁面上顯示出來。使用 submitYahoo 和 submitTeoma 元素可以類似地控制 Submit 按鈕的可見性。
還有一個相當細微的地方需要注意,稍不注意便有可能影響到其後的設計。如 清單 1所示,三個實例的名稱分別為 box、yahoo 和 teoma。請記住,這些名稱都是特定於這些實例數據結構的。並不能把它們當做標識符隨意使用。在本例後面的 switch/case/toggle 機制中重用 teoma 和 yahoo 這兩個 ID 會是個不小的誘惑,但是由於它們的名稱都引用了實例數據所以並不能正常運行。
submission 的定義
清單 2 顯示了 XForm 其余的數據結構。剩下的兩項為 submission 元素。本例使用了兩個 submission 元素,分別對應於不同的搜索。對於這些 submission 元素,有三點需要注意。這三點各自發揮自己的作用,確保“後台搜索”順利進行而不用重新加載頁面。每個 submission 元素都指定了一個 replace 和 instance 關鍵字。請注意:每個 submission 元素都是使用 清單 1 中選定的名稱來引用實例的。這個機制指定:從該 submission 元素返回的數據將替代指定的實例節點中的 XML 數據。這樣便可以清除搜索或者用新搜索結果替代原搜索。請注意,兩個 submission 元素都使用了 ref="instance('box')"。這將指示 submission 動作使用 box 實例的 XML 作為輸入,並且由於此引擎的實質在於使用戶能搜索不同的引擎而無需分別鍵入相同的搜索字符串,因此兩個 submission 元素都引用了相同的輸入數據。Yahoo 特定信息同樣也傳遞給了 Teoma 搜索引擎,但是會被 Teoma PHP 代理忽略。
清單 2. XML 數據結構,第 2 部分 <xforms:submission id="submit_teoma" separator="&"
action="teomaproxy.PHP" method="get"
ref="instance('box')"
replace="instance" instance="teoma"/>
<xforms:submission id="submit_yahoo" separator="&"
action="yahooproxy.PHP" method="get"
ref="instance('box')"
replace="instance" instance="yahoo"/>
</xforms:model>
</head>
兩個 submission 定義中都使用了 PHP 腳本:<engine>proxy.php。這兩個腳本在本質上是相同的,只是基本 URL 和包含的數據有所不同。這些 PHP 腳本使用 cURL 擴展名分別在兩個引擎中檢索 XML 數據,並將數據返回給 XForms。這樣,數據處理實際上就是在 XForms 實現中進行的。清單 3 顯示了 Yahoo PHP 腳本。前面已經說過,Teoma 腳本與它幾乎是相同的。
清單 3. Yahoo 代理腳本<?PHP
$appid = urlencode($_GET['appid']);
if($_GET['site'] != '')
$q = urlencode($_GET['q']." site:".$_GET['site']);
else
$q = urlencode($_GET['q']);
$results = urlencode($_GET['results']);
$url =
"http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=$appid&query=$q&results=
$results";
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, false);
$str = curl_exec($curl);
curl_close($curl);
echo $str;
?>
由於使用 GET 作為 submission 的方法,PHP 腳本就像從傳統 Web 表單中那樣接收此信息。一旦輸入了用於搜索的適當 URL,cURL 將提交該信息並返回結果。
同樣也可以使用 POST 方法,但必須確保正確地處理 URL 編碼數據。當提交的對象是搜索引擎 API 時,也可以直接向表單提交 XML。例如,當 MSN Live Search API 處理 SOAP 事務時。清單 4 顯示了如何處理用 POST 提交的信息。
清單 4. 使用 POST 的 Yahoo 代理 PHP 腳本<?PHP
$XML = $HTTP_RAW_POST_DATA;
$doc = new DomDocument('1.0');
$doc->loadXML($XML);
$appid = $doc->getElementsByTagName("appid")->item(0)->nodeValue;
$query = $doc->getElementsByTagName("q")->item(0)->nodeValue;
...
接下來我們回到表單本身
XForms 表示
XForms 表示包括兩個基本的部分:輸入部分和輸出部分。這兩個分開的部分相互之間進行交互,為完全不同的底層數據結構提供了一個無縫的接口。這些底層數據結構由各自的搜索引擎定義,並不受 XForms 創建者控制。清單 5 顯示輸入部分中的第一部分。
XForms 輸入表示
在 XForms 表示的輸入部分中,需要注意用於把搜索文本和 box 實例中的 q 節點鏈接起來的方法和 submit 定義中 toggle 元素。這些 toggle 代碼行用於控制輸出部分中兩種情況之間的轉換。前面我們提到過應注意定義的名稱,這裡顯得格外重要。本例中,Teoma 搜索的情形被命名為 teo 而不是 teoma,這樣便不會與實例名相沖突。Yahoo 提交也使用了類似的命名方法。
清單 5. XForms 表示代碼,第 1 部分 <body>
<p>
<xforms:select1 ref="instance('box')/engine">
<xforms:label>Choose a search engine: </xforms:label>
<xforms:item>
<xforms:label>Yahoo</xforms:label><xforms:value>Yahoo</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Teoma</xforms:label><xforms:value>Teoma</xforms:value>
</xforms:item>
</xforms:select1>
</p>
<p>
<xforms:input ref="instance('box')//q" >
<xforms:label>Search Text: </xforms:label>
</xforms:input>
<xforms:input ref="instance('box')/site" >
<xforms:label>Site: </xforms:label>
</xforms:input>
<xforms:select1 ref="instance('box')/results" >
<xforms:label>Results displayed: </xforms:label>
<xforms:item>
<xforms:label>Ten</xforms:label>
<xforms:value>10</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Twenty</xforms:label>
<xforms:value>20</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Thirty</xforms:label>
<xforms:value>30</xforms:value>
</xforms:item>
</xforms:select1>
</p>
<p>
<xforms:submit submission="submit_teoma" ref="instance('box')/submitTeoma">
<xforms:label>Search using Teoma</xforms:label>
<xforms:toggle ev:event="DOMactivate" case="teo"/>
</xforms:submit>
<xforms:submit submission="submit_yahoo" ref="instance('box')/submitYahoo">
<xforms:label>Search using Yahoo!</xforms:label>
<xforms:toggle ev:event="DOMactivate" case="yah"/>
</xforms:submit>
</p>
請注意,表單列出了所有的信息,包括站點特定的字段。還要注意頁面同時列出了兩個按鈕。然而,數據的真實外觀是由頂部的選擇列表控制的 engine 字段值決定的。當值為 Yahoo 時,顯示的是所有的 Yahoo 信息,如 圖 1 所示。當值為 Teoma 時,顯示的是所有的 Teoma 信息,如圖 2 所示。
圖 2. 使用 Teoma 搜索
XForms 輸出表示
XForms 的幾個很有用的特性,例如“repeat”和“switch/case”特性,使兩種格式不同的搜索結果無縫統一地表示了出來。清單 6 顯示了 XForms 表示的輸出部分。
清單 6. XForms 表示代碼,第 2 部分 <xforms:label><h3>Search Results:</h3></xforms:label>
<xforms:switch>
<xforms:case id="teo" selected="true">
<xforms:repeat nodeset="instance('teoma')//RESULT">
<p>
<xforms:label><b>Title:</b> <xforms:output
ref="TITLE"/></xforms:label><br/>
<xforms:label><b>URL:</b> <xforms:output
ref="URL"/></xforms:label><br/>
<xforms:label><b>Description:</b> <xforms:output
ref="ABSTRACT"/></xforms:label>
</p>
</xforms:repeat>
</xforms:case>
<xforms:case id="yah">
<xforms:repeat nodeset="instance('yahoo')//y:Result">
<p>
<xforms:label><b>Title:</b> <xforms:output
ref="y:Title"/></xforms:label><br/>
<xforms:label><b>URL:</b> <xforms:output
ref="y:Url"/></xforms:label><br/>
<xforms:label><b>Description:</b> <xforms:output
ref="y:Summary"/></xforms:label>
</p>
</xforms:repeat>
</xforms:case>
</xforms:switch>
</body>
</Html
首先應注意 switch 語句,它與附帶的 case 語句結合使用。前面的部分討論了兩個 submission 元素並重點研究了每個 submit 定義中的兩條語句,該語句引用了與 case 語句引用的相同的標志,即 teo 和 yah。toggle 動作使在任意給定時間都有其中的一個 case 有效。作用是只顯示附帶的 XForms 定義中的一種。
在 case 語句中,Yahoo 與 Teoma 輸出顯示的基本格式非常相似。搜索引擎運行時,將生成很多 results 節點。對於大部分搜索引擎 API 而言,返回的搜索項數目是可配置的。因此,只要對此接口進行改進使其支持該特性,便可以改變 results 節點的數目。XForms 對從 XML 數據結構中找到的所有節點都將運行 "repeat"。這樣便創建了一列結果,而無需 XForms 創建者預先知道它的數目,也無需使用 JavaScript 修改頁面源數據結構來為每個節點提供顯示元素。
圖 3 顯示了使用 Teoma 產生的搜索結果。Teoma 的情況比較簡單一點。Web 站點記錄了 Teoma 返回的 XML 結構(參閱 參考資料 中的鏈接)。因為 Teoma 並沒有為數據指定名稱空間,所以只能使用 XML 中給定的名稱來引用節點。
圖 3. Teoma 搜索結果
圖 4 顯示了使用 Yahoo 得到了搜索結果。考慮到 Yahoo 能夠將結果限定在一個站點之內並且能決定顯示多少結果,Yahoo 的 XML 字段名稱與 Teoma 有所不同。不過它們之間還有另一個重要的區別:Yahoo 在返回的 XML 中指定了名稱空間。這需要 XForms 引用使用該名稱空間。XHtml 在其頭部把名稱空間定義為 y。清單 4 用粗體顯示了所有 XForms ref 說明符上的這些 y: 說明符。
圖 4. Yahoo 搜索結果
結束語
XForms 在顯示方式這方面具有極大的靈活性,能動態地適應不同的情況,以提供無縫的用戶體驗。本文使用其中的一些功能創建了一個搜索客戶機,它能根據用戶選擇的搜索引擎顯示不同的界面。可以在自己的基於 XForms 的應用程序中使用這些思想,創建能適應用戶選擇和可用數據的接口。