簡介:在我們的前一篇文章結合使用 Ajax 和 WebSphere Portal 中,我們討論了在門戶應用程序中 使用 Ajax 時的一些問題和設計關注事項。在本教程中,我們將把新發現的知識投入使用,並創建一個 Ajax Portlet 應用程序。為了增加趣味,我們決定構建一個大量使用 Ajax 和 DHTML 的 Portlet。這 可以使您初步了解該技術的功能,同時還為您提供一個用於浏覽數據庫的有用工具。雖然該應用程序的 大部分都是已經編寫好的,但是您將負責填補未編寫好的部分以使其正常運行。
開始之前
在本教程中,您將為特定於 Ajax 的調用編寫代碼,檢查操作頁面的 DHTML 的代碼,並完成從浏覽 器到服務器的完整往返 Ajax 調用的過程。整個應用程序包括幾百行代碼,但是我們僅要求您編寫重要 組件的代碼,其余代碼將由我們提供。相關步驟包括:
編寫用於創建 XMLHttpRequest (XHR) 和處理 XML 文檔對象的浏覽器無關的代碼。
向服務器發送 Ajax 請求並操作所返回的結果。
檢索並操作 Portlet 配置中的 Servlet 上下文,以便動態訪問 Portlet .war 文件中包括的 Ajax Servlet。
使 JavaScript 事件能夠處理操作並顯示或更新 JSP 頁上的數據。
將該 Portlet 應用程序部署到 WebSphere® Portal 上並查看結果。
我們還將檢查用於操作此特定應用程序數據的代碼,包括如何:
在異步 Ajax 調用期間啟用和禁用表單元素。
在通過 Ajax 調用檢索到新數據以後,使用 DHML 來更新選擇框。
使用 innerHTML 來替換網頁的部分,從而動態更新 HTML <div> 區域標記。
設置
本教程假設您將使用 Rational® Application Developer (RAD) 或其系列成員之一,但是您可 以使用自己喜歡的任何工具來編輯和部署該門戶應用程序。本練習中的所有內容都與特定的工具無關。 您還需要安裝 WebSphere Portal v5.x 或更高版本,以及一個可用的數據庫,當然,我們假設該數據庫 是 DB2®。
為了使所有這些部分協同工作,您需要將應用程序代碼導入一個 RAD 門戶項目,並在 WebSphere 中 至少創建一個數據源。以下步驟將幫助您著手開始此任務。
配置項目並導入文件
在 RAD 中創建一個 JSR 168 Portlet 項目。
啟動 RAD。
選擇 File > New > Other,然後定位並選擇 Portal > Portlet Project (JSR 168)。注 意:如果您沒有看到 Portal 部分,請選中 Show All Wizards。
圖 1. JSR 168 Portlet 項目
單擊 Next。
輸入如下所示的信息。
Name:Ajax_and_Portal
Project location:接受缺省值。
WebSphere Portal version:選擇您的 WebSphere Portal 版本。
Create a portlet:取消選中此復選框。
圖 2. 新的 Portlet 項目定義
單擊 Finished。
將所提供的源文件導入新項目。
右鍵單擊最頂層的“Ajax_and_Portal”項目。
選擇 Import...> Import ...
選擇 Zip file。
單擊 Next。
圖 3. 從 zip 文件導入
在 zip 文件導入頁上,執行以下操作:
Into folder:確保選擇 Ajax_and_Portal 項目。
Overwrite existing resources without warning:選中此復選框。
圖 4. Select zip file to import 屏幕
單擊 Finished。
您的項目結構現在應與圖 5 所示類似。
圖 5. 導入的項目結構
初始部署和測試
要做的第一件事情是以其當前狀態部署該應用程序。有關使用 RAD 將 Portlet 應用程序部署到門戶 中的說明,請參考附錄 B。在部署該 Portlet 並將其放到某個頁面上以後,我們希望查看一下它。
圖 6. 應用程序網頁
嘗試查找一個數據源。什麼也沒有發生!不是非常激動人心,是吧?到本練習結束時,基於對服務器 的 Ajax 調用和用於更新 HTML 標記的 JavaScript 代碼,該頁面上的所有這些項將執行操作或顯示新 數據。
請注意,Context Path 在該 Portlet 的頂部。很快您將需要上下文路徑來直接從浏覽器中調用該 Servlet。在上面的示例代碼中,上下文路徑為: /wps/PA_dsllabu.
它真的是個 Portlet,絕不是開玩笑。
是的,它是一個 Portlet。並且它完全符合 Java® Portlet (JSR168) 規范。然而,Ajax 本身 是一項獨立的技術,本練習將演示此項技術,並且它碰巧使用一個 Portlet 來顯示數據。這非常重要, 因為我們還包括了一個 Servlet,它檢索 Portlet WAR 文件中的應用程序的數據。WebSphere Portal 將把該 Portlet 以及 Servlet 作為 Web 應用程序部署到 IBM WebSphere Portal 中。這為該 Portlet 提供了動態訪問其應用程序所附帶的 Servlet 的能力。不必在另一個 J2EE 應用程序中單獨部署它。
讓我們首先看一下 Portlet 代碼。
定位到頁面左側的 Project Explorer。展開 Ajax_and_Portal 項目並一直展開到 DBExplorerPortlet.java 文件。通過圖 7 來確定該文件的位置和樹結構。
圖 7. DBExplorerPortlet.java 位置
雙擊該文件以在編輯器視圖中打開它。仔細浏覽該代碼,並注意它除了顯示該 Portlet 的 JSP 頁以 外,其他什麼也不做。下面顯示了 doView() 方法,作為您應該看到的內容的示例。public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
// ------------------------------------------------------------
// Note: The portlet is just diplaying a JSP for the AJAX Lab.
// It does absolutely nothing else.
// ------------------------------------------------------------
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
// Invoke the JSP to render
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(JSP_FOLDER + VIEW_JSP);
rd.include(request,response);
}
Ajax 技術通過 JavaScript 和 DHTML 來使用其功能。這個特定的示例並不需要任何預先確定的 JSP 數據;因此,該 Portlet 本身除了顯示該 JSP 外,其他什麼也不做。
我告訴過你們,它是一個普通 Servlet
創建 Servlet 是非常簡單的事情。您已經做過幾百次了。只需實現您的服務方法 (doGet/doPost) 和所需的任何業務邏輯。在通常情況下,您會希望向調用方返回一個 XML 文檔,但是您可以向客戶端返 回任何文本信息。讓我們看一下所提供的 Servlet 代碼,以了解用於 Ajax 調用的數據來自何處。
在 Project Explorer 下面,將樹形結構展開到該 Servlet 的代碼。該文件名為 com.ibm.examples.Ajax.servlets > DBExplorerServlet.java。雙擊該 Java 文件以在編輯器中打 開它。
注意該 Servlet 的 doPost() 方法及其完成的任務。此特別代碼並不特定於 Ajax,但它是使用 XMLHttpRequest 對象發送的參數來獲得一個 Servlet 以響應請求的方法之一,本練習稍後將對 XMLHttpRequest 對象進行解釋。
如果您希望更好了解它所做的工作,可以隨便浏覽該代碼。
向該 Servlet 請求的每一組參數都導致它使用唯一的 XML 結果來作出響應。然後浏覽器端 JavaScript 分析響應數據,並根據需要呈現該數據。例如,如果向該 Servlet 發送參數 “LIST=SCHEMAS”,則它將以 XML 格式返回數據庫模式列表。讓我們試驗一下,只是為了證 明該 Servlet 代碼並沒有什麼特別之處。
在您的浏覽器窗口中輸入以下 URL。圖 8 顯示了一個示例。 http://localhost:9081/wps/PA_dsllabu/DBExplorerServlet? DS=jdbc/sample&LIST=SCHEMAS
圖 8. 直接調用 Servlet 的結果
注意: 您需要調整實際 URL 以匹配您的部署。我們的示例 URL 的 /wps/PA_dsllabu 部分是您在第 一次部署 Portlet 應用程序時由門戶定義的。若要查看您的門戶上下文,請查看我們前面部署的 Ajax Portlet 的頂部。
該 Servlet 是一個非常簡單的 J2EE Servlet。您可以通過浏覽器訪問它,就像訪問任何通常通過 WebSphere Application Server 界面來部署的其他 Servlet 一樣。WebSphere Portal 使用各自的唯一 URI 來部署 WAR 文件,以便 Portlet 和其他 Web 組件都能訪問該特定應用程序的文件,例如圖像、 JavaScript、級聯樣式表和我們示例中的 Servlet。由於該 Servlet 的部署方式與任何其他 Web 應用 程序一樣,因此它在部署時還具有與 Portlet 應用程序相同的 Web 上下文。
在將 Servlet 與 Portlet 應用程序捆綁在一起時,唯一的真正訣竅是將它注冊到 web.xml 文件中 。此任務與在通常的 Web 應用程序中完全一樣。
下面是取自我們練習中的 web.xml 文件的片段。需要重點注意的項是 Servlet-mapping > url- pattern。在本例中,它是 /DBExplorerServlet。當我們定義 JavaScript 變量(Ajax 調用將通過該變 量與 Servlet 進行通信)時,將會使用到此值。繼續查看我們項目的部署描述符。圖 9 顯示了一個示 例。
圖 9. 具有捆綁 Servlet 定義的 Portlet web.xml 文件
定義用於與該 Servlet 通信的 JavaScript 變量
為了使應用程序知道如何動態調用我們的 Servlet(因為在部署它之前,您不知道 URI 上下文), 您需要編寫一些代碼來動態定義該 Servlet 的 URL。
在 Project Explorer 中,打開以下文件:Ajax_and_Portal > WebContent > jsp > DBExplorer_View.jsp。通過圖 10 在樹形結構中定位該文件。
圖 10. 查看 JSP 文件位置
選擇 Source 選項卡(如果還沒有選擇的話),定位到注釋文本“Insert JavaScript for the Ajax_SERVLET servlet URL here”,並按如下所示編寫該 JavaScript 部分的代碼。使用下面的 代碼示例來確定所插入代碼的位置和要在應用程序中輸入的實際代碼。<%--
-------------------------------------------------------------------------------------
Set AJAX response servlet URL variable
-------------------------------------------------------------------------------------
Placing this variable in the JSP allows for runtime compile updates to the javascript
We are using encodeURL() and getContextPath() to get the path of the web application
deployed as a WAR file in Portal for accessing the servlet, stylesheet and javascript
-------------------------------------------------------------------------------------
--%>
<%--------------- Insert JavaScript for the AJAX_SERVLET URL here --------%>
<script type="text/javascript">
var AJAX_SERVLET = "<%=renderResponse.encodeURL(renderRequest.getContextPath())% >"
+ "/DBExplorerServlet";
</script>
<%------------------------------- End JavaScript insert ------------------% >
您必須將此代碼放在 JSP 中,以便門戶運行時能夠使用 Portlet 對象(renderResponse 和 renderRequest)來確定在部署 Portlet 應用程序時生成的 Servlet 上下文。JavaScript 中的 Ajax 函數現在可以使用此變量來對該 Servlet 發出調用,以檢索動態更新頁面所需的數據。
仔細查看該 JSP 文件的其他部分。下面是一些要查看的關鍵點:
所有表單字段都沒有被分配任何事件處理程序(例如,onclick="event()")。我們將在 運行時使用 JavaScript 來注入它們。
不存在實際的 <form> 標記,也不存在向服務器提交表單輸入字段內容的方法。同樣,這將由 Ajax 調用來處理。
就在該文件的結尾處,有一個很小的 Javascript 塊,該塊調用了一個 "init()" 函數。 此函數向我們的輸入字段添加事件偵聽器。在普通 Web 應用程序中,此函數作為 body 元素的 “onload”事件來調用。但是,由於這是一個 Portlet JSP 片段,我們將不使用 body 標記 。因此,需要在將 HTML 內容加載到 DOM 中以後才發出此調用。
通過選擇頂部菜單上的 File -> Save 或按 Ctrl-S 來保存 JSP 更改。
那麼 Ajax 代碼在何處呢?
這個問題問得好!讓我們開始編寫實際的代碼吧。為了使 Ajax 正常工作,您需要從客戶端可能使用 的任何浏覽器中檢索並使用 XMLHttpRequest 對象。為此,可以編寫一個 JavaScript 函數來獲取浏覽 器無關的 XMLHttpRequest 對象。不同的浏覽器以不同的方式實例化該 XHR 對象。
在 Project Explorer 中,打開以下文件:Ajax_and_Portal > WebContent > js > DBExplorer.js。
定位到 getXMLHttpRequest 函數。將以下代碼插入腳本中所指示的行上。function getXMLHttpRequest() {
var xhr = null;
// ---- Insert code here ----
if ( window.XMLHttpRequest ) {
// Mozilla/Safari
xhr = new XMLHttpRequest();
} else if ( typeofActiveXObject != "undefined" ) {
// IE
xhr = new ActiveXObject("MicroSoft.XMLHTTP");
}
// ---- End insert code ----
return xhr;
}
此函數對於使浏覽器無關的 Ajax 正常工作非常關鍵,因為它確定浏覽器是支持 XMLHttpRequest 還 是支持 ActiveXObject (Microsoft.XMLHTTP) 對象。此函數返回正確的對象,具體取決於所支持的浏覽 器類型。一旦創建了該對象,API 本身就是相同的,但是不同的浏覽器支持不同的實例化方法,並需要 唯一的調用以創建每個對象。此函數可處理大多數浏覽器。
配置 Ajax 調用並檢索數據
現在您可以獲得實際的 XMLHttpRequest 對象,以向服務器發出 Ajax 回調,您需要創建將實現此行 為的函數。
為了理解接下來的部分中的代碼,您需要理解為 XMLHttpRequest 調用指定的不同狀態。在 JavaScript(在該文件的頂部)中,為便於本練習稍後的工作,我們已作為變量添加了該代碼。下面的 代碼片段顯示了本練習中使用的代碼的一個示例。您不 必編寫本練習的此部分代碼,因為它已經在腳本 中了。這僅供參考。
// -------------------------------------------------------------------------
// Set global ready state and HTTP variables to check AJAX status
// -------------------------------------------------------------------------
var READY_STATE_UNITIALIZED=0;
var READY_STATE_LOADING=1;
var READY_STATE_LOADED=2;
var READY_STATE_INTERACTIVE=3;
var READY_STATE_COMPLETE=4;
var HTTP_OK = 200;
下一步,我們需要創建向服務器發出 XMLHttpRequest 調用並請求特定數據的代碼。
定位到 event_setDataSource 函數。按下面的指示插入以下代碼。function event_setDataSource() {
log("event_setDataSource: starting", "#660");
// ... Existing code removed for brevity...
// XHR for Schemas
log("event_setDataSource: getting schemas", "#660");
// ---- Insert code here ----
xhr_getSchemas = getXMLHttpRequest();
xhr_getSchemas.onreadystatechange = callback_getSchemas;
xhr_getSchemas.open("POST",AJAX_SERVLET,true);
xhr_getSchemas.setRequestHeader ("Content-Type", "application/x-www-form- urlencoded");
|--10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters --------- |
xhr_getSchemas.send("LIST=SCHEMAS&" + getFormDataParams() );
// ---- End insert code ----
log("event_setDataSource: finished", "#660");
}
這個特定的函數用於向 Ajax Servlet 發出調用並請求數據庫中的模式列表。此腳本中還有若干個已 經為您編寫好的其他函數,它們發出類似的調用,但是由於本練習的范圍所限,您應該集中於這個特定 函數。此代碼部分創建一個 XHR 對象,分配一個回調函數,並打開一個到 AJAX_SERVLET(在前面的 JSP 中定義)的 POST 連接。最後,它發起一個請求,並傳入一些參數。確定用於 XMLHttpRequest 對 象的以下方法及其功能。
方法名稱 功能 .onreadystatechange 告訴要在請求狀態更改時調用哪個 JavaScript 函數。(您將在接下來的部分中編寫 此代碼。) .open 設置請求方法(在我們的示例中為 POST)、URL,並確定浏覽器是應該異步發出調用 (true) 還是等待結果 (false)。此示例不需要浏覽器等待響應,因此我們將其設置為 true,表示異步 調用。 .setRequestHeader 告訴該 Servlet 請求中發送的是什麼類型的內容。在此示例中,調用將包含 HTTP 表 單參數。 .send 發起對該 Servlet 的調用並將參數附加到請求中。
用於其他表單字段的以下函數已經為本練習編寫好了,不需要再添加它們。它們使用與上面添加的代 碼類似的代碼來更新屏幕的其他區域。這僅供參考:
event_setSchema
event_setTable
event_addColumn
event_deleteColumn
event_getTableData
由於您在前一步中告訴 XMLHttpRequest 對象您將使用一個狀態處理程序,因此您需要編寫回調函數 代碼,並讓它在狀態更改時執行操作。定位到 callback_getSchemas 函數,並在所指示的區域插入以下 代碼。function callback_getSchemas() {
handlerLog("callback_getSchemas", xhr_getSchemas, "#00c");
// ---- Insert code here ----
// Show object is updating if the ready state is loading
if (xhr_getSchemas.readyState == READY_STATE_LOADING) {
showSelectUpdating("schema_select");
}
// Handle results if the ready state is complete
if (xhr_getSchemas.readyState == READY_STATE_COMPLETE) {
if (xhr_getSchemas.status == HTTP_OK) {
fillSelect("schema_select", xhr_getSchemas.responseXML, true);
formUnlockField("schema_select");
} else {
error("Failed to retrieve schema list:" + xhr_getSchemas.status);
}
}
// ---- End insert code ----
}
請注意,此代碼中存在兩個不同的部分。如果請求狀態更改為 "READY_STATE_LOADING"( 或 1),則它更新頁面上的數據,從而指示已發出請求並且正在更新數據。該應用程序的此部分是已經 為您編寫好的。最好顯示正在為 Ajax 相關調用而更新的數據,因為您不知道網絡連接速度將會如何, 或者返回數據將需要多長時間。設置一個“更新”指示器可以在浏覽器等待響應時讓最終用 戶了解活動情況。如果沒有指示器,最終用戶可能以為什麼事情也沒有發生,當響應非常慢時尤其如此 。
該函數的第二部分將在請求狀態更改為 "READY_STATE_COMPLETE"(或 4)時執行。這意 味著請求已完成,並且通常已經為處理結果准備就緒。這個 fillSelect 函數接受結果 XML 文檔,並用 返回數據來更新 HTML select 標記。用於此頁上其他字段的以下函數已包括在該文件中,不需要再添加 它們。它們使用與上面添加的代碼類似的代碼來更新屏幕的其他區域。這僅供參考:
callback_getDatabaseProperties
callback_getTables
callback_getColumns
callback_getTableData
在繼續之前,通過選擇 File > Save 或按 Ctrl-S 來保存該文件。
啟動代碼
現在您已經准備好所有 Ajax 代碼,但它不會自己啟動。您需要告訴浏覽器您實際希望在執行某個操 作時發生什麼事情。您需要編寫一些 JavaScript 事件,以便最終用戶能夠在與應用程序交互時啟動 Ajax 調用。在本部分中,您將向“數據源查找”按鈕添加“onclick”事件。
定位到 DBExplorer.js 文件中的 init() 函數。通過將以下代碼插入腳本中所指示的行上,從而為 “加載數據源”按鈕添加一個事件處理程序。document.getElementById ("datasource_button").onclick =event_setDataSource;
這告訴浏覽器在該按鈕被單擊時執行此函數。我們原本可以將這些事件偵聽器直接添加在表單輸入標 記中,但那樣不是非常有趣。當使用此方法添加事件偵聽器時,您添加的是實際函數對象,因此不包括 引號或括號。其他字段的事件代碼已經為您編寫好了。下表包含已經為您編寫好的其他 “onXXX”事件,您無需執行任何操作。如果您希望獲得更好的了解,可以查看代碼的這些部 分。
輸入字段 事件 處理程序函數 執行的操作 數據源加載按鈕 onclick event_setDataSource 檢索數據庫屬性和模式列表。 Schema select onchange event_setSchema 檢索表列表。 Table select onchange event_setTable 檢索可用列。 Available columns select ondblclick event_addColumn 向活動列列表添加列並檢索表數據。 Active columns select ondblclick event_deleteColumn 從活動列列表刪除列並檢索表數據。
在繼續下一步之前,通過從菜單選擇 File -> Save 或按 Ctrl-S 來保存更改。
部署和測試應用程序
現在您已經編寫好 JavaScript 的 Ajax 部分的代碼,並告訴 JSP 在查找數據源時執行該代碼,您 需要將完成的應用程序部署到服務器。
現在已經部署了該 Portlet,您需要測試該應用程序。
將浏覽器指向包含我們的 Portlet 的門戶頁。
首先輸入一個數據源名稱,或者接受缺省值“jdbc/sample”,並單擊 Lookup 按鈕。請 注意,其他項開始根據數據源值和您放在 JSP 中的 onclick 事件代碼來動態更改信息。一個日志區域 顯示了正在發生的各個活動。
那麼究竟發生了什麼呢?當您輸入數據源並單擊 Lookup 按鈕時,將激發 onclick 事件,並導致調 用“event_setDataSource”函數。此函數啟動兩個 XHR Ajax 進程來從服務器檢索數據。一 旦接收到來自服務器的響應,則由各個“callback_XXX”函數對它們進行處理。然後分析接 收到的 XML 數據,並將其用於填充文檔的各個區域。此活動完全異步進行!如該頁頂部的時間戳所示, 不存在門戶頁刷新,而該頁上的多個項得到了更新。這就是 Ajax!
現在選擇某個模式,例如 DB2INST1。各個字段會自動更新,以僅顯示為該特定模式定義的表。
從可用表列表中選擇某個表。例如,如果您選擇 DB2INST1 作為模式,則可以選擇 EMPLOYEE 作為表 。當您這樣做時,該特定表的列將在表列表下面的選擇框中變得可用。
雙擊一些列以將它們添加到所選的列列表。如果您選擇 EMPLOYEE 表,則可以將 EMPNO、FIRSTNAME 、MIDINIT 和 LASTNAME 添加到列列表中。請注意數據表如何自動更新以反映所選的列。
雙擊 Active columns 列表中的條目以刪除它們。數據表將更新以反映新的選擇。記住,此活動完全 在沒有頁刷新或任何門戶交互的情況下進行!以下是在使用 Ajax 發出調用以後用結果數據來填充的整 個頁面的示例。
圖 11. 實際操作中的演示應用程序
您可以試驗和使用不同的數據源並探索您的數據。試驗,嘗試不同的事情。如何增強該應用程序?
故障排除
大功告成!您現在已經是 Ajax 技術和門戶開發方面的專家了。什麼?某個地方無法工作?您有幾種 選擇。
返回去檢查您的所有代碼更新。Ajax_and_Portal zip 文件中還提供了一個名為 solutions.txt 的 替代文件。請將其中包括的源代碼塊與您代碼中的內容進行比較。
使用 Mozilla Firefox 浏覽器以及 FireBug 擴展來調試 JavaScript。由 Joe Hewitt 編寫的 Firebug 很容易學習,並且可以快速幫助您跟蹤代碼中的任何錯誤。花 30 分鐘使用這個令人驚異的工 具,您將節省數小時甚至數天的時間,並試著弄清楚任何 JavaScript 相關的錯誤。
檢查門戶日志文件中由該 Servlet 生成的錯誤消息。常見錯誤通常與到數據庫的數據源連接有關。
作為最後的手段,您還可以向作者發送電子郵件,並詳細描述所發生的情況,我們將盡最大努力幫助 您糾正錯誤。
屏幕更新背後的神話
就 Ajax 而言,您發出調用以獲得數據,並使用返回數據來更新頁面。這就是對 Ajax 的簡單描述。 如果您希望繼續探索在更新數據時使用的某些 DHTML,您可以繼續該練習。在以下部分中,您將了解如 何動態更新 HTML 對象。這些只是您在檢索數據以後所能做的工作的一些示例。該練習的此部分主要是 DHTML 示例,並且是可選的。我們將介紹:
使用 DOM 動態更新 HTML select 內容
使用 innerHTML 動態更新 HTML DIV 內容
禁用表單字段並在完成 Ajax 調用後重新啟用它們
將表單數據作為 Ajax 調用中的參數來發送
日志記錄區域
使用 DOM 動態更新 HTML select 內容
使用前面描述的方法,定位到 DBExplorer.js 文件並找到函數 fillSelect()。
// ------------------------------------------------------------------------------ -------
// Function to fill a specific select object from a properly formatted XML document
// -------------------------------------------------------------------------------------
function fillSelect( id, xmlResults, addAny) {
clearSelect(id);
selectObj = document.getElementById(id);
if ( selectObj ) {
if (addAny) {
selectObj.options[0] = new Option("All Schemas", "any");
}
var xmlRoot = xmlResults.getElementsByTagName("htmlselect")[0];
if (xmlRoot) {
for(var i=0; i < xmlRoot.childNodes.length; i++) {
child = xmlRoot.childNodes[i];
if (child.nodeName == "select") {
attrs = child.attributes;
selectObj.options[selectObj.length] = new Option(
child.firstChild.nodeValue, attrs.getNamedItem("value").value);
}
} //for
}
if (selectObj.length==0) {
selectObj.options[0] = new Option("Not Available", "None");
selectObj.disabled = true;
}
if (selectObj.options[0]) {
selectObj.options[0].selected = true;
}
} // if obj
}
注意該代碼如何檢查 Ajax Servlet 發送的 XML 中的標記元素 htmlselect 和分析 select 標記以 獲得用於更新選擇選項的數據。如果您在本練習開頭直接訪問過該 Servlet,您會注意到返回的 XML 的 格式非常熟悉。當在 XML 中找到一個 select 標記時,則會在選擇列表上創建一個新選項。這允許在使 用 clearSelect(id) 方法(此方法也包括在 JavaScript 中)來清除列表後動態更改選擇框。
使用 innerHTML 動態更新 HTML DIV 內容
定位到 DBExplorer.js 文件中的函數 updateDivHtml()。
// ------------------------------------------------------------------------------ -------
// Function to update a specific DIV object from an XML Document
// -------------------------------------------------------------------------------------
function updateDivHtml(xmlDoc, divName) {
var divHtml = "<table class='property_table'>";
var xmlRoot = xmlDoc.getElementsByTagName("htmldiv");
if (xmlRoot) {
// Limit to only the first matching element.
// Should only ever be one, but still need to state it
xmlRoot = xmlRoot[0];
if (xmlRoot.childNodes.length == 0) {
divHtml = createDivRow("No data available","");
} else {
for(var i=0;i<xmlRoot.childNodes.length;i++) {
textLine = xmlRoot.childNodes[i];
if (textLine.nodeName=="textline") {
attrs = textLine.attributes;
if (attrs && textLine.firstChild) {
divHtml += createDivRow(attrs.getNamedItem("label").value,
textLine.firstChild.nodeValue);
}
}
}
} // if
}
divHtml += "</table>";
document.getElementById(divName).innerHTML = divHtml;
}
此示例以不同的方式更新 DIV 標記。其相似之處在於它也分析返回的 XML,但是該 XML 具有不同的 格式。它以表格格式動態創建 HTML 以插入 DIV。請注意,該函數的最後一行使用在分析期間創建的表 HTML 來更新 DOM 元素的 innerHTML 屬性。這將 DIV 中的 HTML 完全動態替換為新格式化的 HTML,並 更新屏幕。
禁用表單字段並在完成 Ajax 調用後重新啟用它們
位於 Ajax_and_Portal.js 文件中的接下來兩個函數在發出 Ajax 調用時啟用和禁用頁上的表單對象 。當有多個項可能在單個請求期間更改時,這可以減少可能出現的問題數量。通過禁用該字段,您可以 確保最終用戶在等待來自 Servlet 的請求時,他們不會更新任何其他可選擇項。
// ----------------------------------------------------------------------------
// Functions to lock/unlock form objects. Used when processing
// requests so the user doesn't try to update the field when the
// requests are not finished processing.
// ----------------------------------------------------------------------------
function formLockField( id ) {
document.getElementById( id ).disabled = true;
}
function formUnlockField( id ) {
document.getElementById( id ).disabled = false;
}
典型的邏輯流是讓事件處理程序函數禁用將在 Ajax 調用期間被更新的任何字段。一旦收到 Ajax 響 應,則解鎖相應的表單字段就是回調函數的責任。
將表單數據作為 Ajax 調用中的參數來發送
將表單數據作為參數傳遞,就像某個 HTML 表單發出的普通請求一樣。在此代碼中,對於每個 DOM 對象(它們具有我們希望作為 Ajax 調用一部分來發送的信息),我們對其進行編碼並放在參數字符串 中,以將其與請求一起發送。注意該代碼還隨請求一起發送一個時間戳。這促使浏覽器不將 URL 緩存到 Serlvlet,以防止存在多個針對同一系統的請求。
// ------------------------------------------------------------------------------ -------
// Function to retrieve all of the form data to send to the AJAX servlet as parameters
// -------------------------------------------------------------------------------------
function getFormDataParams() {
var params = "DS=" + encodeURIComponent( document.getElementById ("get_ds").value )
+ "&SCHEMA=" + encodeURIComponent(getSelectedOption ("select_schema"))
+ "&TABLE=" + encodeURIComponent(getSelectedOption ("select_table"))
+ "&COLUMNS=" + getOptionList("selected_columns")
+ "&NOCACHE=" + new Date().getTime();
log("Form params: " + params);
return params;
}
日志記錄區域
我們的應用程序中有一個部分提供了用於對事件進行日志記錄的窗口,以便更好地跟蹤流程流。這是 一個將要實現的簡單子系統。工具包通常提供了類似的功能,不過這對我們的練習來說已經足夠了。
首先,打開 css > DEBExplorer.css 文件,並定位到位於該文件底部附近的 div.logger 和 stanza。
div.logger {
height: 200px;
width: 100%;
font-family: monospace;
font-size: small;
background-color: #eaeaea;
overflow: scroll;
border: 1px solid #000099;
}
可以看到,我們在創建一個具有固定高度的可滾動 DIV 區域。這導致在我們向日志記錄區域頂部添 加新的日志消息時,內容將向下滾動。下一步,打開 DBExplorer.js 文件,並定位到位於該文件底部附 近的日志記錄函數。
// ------------------------------------------------------------------------------ -------
// update logging window with messages
// -------------------------------------------------------------------------------------
function log(msg, color) {
var logger = document.getElementById("logger");
if ( color == null ) {
color = "black";
}
if ( logger ) {
var d = new Date();
var time = (d.getHours() + ":" + d.getMinutes() + ":"
+ d.getSeconds()).replace(/\b(\d)\b/g, '0$1');
logger.innerHTML = "<span style='color:"+color+"'>"
+ time + ": " + msg + "</span><br />" + logger.innerHTML;
} else {
alert("No logging area found! Log message is:\n" + msg);
}
}
在該函數中,我們使用其 ID 屬性獲得一個對日志記錄程序 DIV 的引用。我們希望向每個消息添加 很好地格式化的時間戳,這是所有日志都應該有的,因此我們創建一個 Date 對象,並獲得所需的字段 。所示的替換正則表達式只是向單詞邊界中的任何一位數插入一個前導零(例如, “3:47:2”將轉換為“03:47:02”)。最後,在日志記錄區域中存在的現有內容 前面,我們插入具有所請求顏色的日志消息。請注意,innerHTML 屬性在同一上下文中既是可寫又是可 讀的。
附錄 A. 配置示例數據庫和數據源
我們的應用程序的缺省數據源是 DB2 的通用“Sample”數據庫,它被定義為名為 “jdbc/sample”的數據源。如果您知道應用程序服務器中存在的其他數據源的名稱,您也可 以使用自己的數據源名稱來改寫缺省數據源名稱。讓我們繼續配置該示例。
首先,讓我們創建該示例數據庫。DB2 使得此過程非常容易。要麼運行 DB2 的“First Steps”並選擇 Create sample database,要麼作為 DB2 實例用戶從命令行運行 db2sampl。如果 您需要更多幫助,請參考 DB2 入門文檔。
其次,我們需要為該示例數據庫定義一個數據源。啟動您的主 WebSphere Application Server,它 通常是 server1。
登錄到管理員控制台:
對於 WebSphere Application Server v5:http://localhost:9090/admin
對於 WebSphere Application Server v6:http://localhost:9060/ibm/console
創建一個新的 JDBC 驅動程序提供程序:
選擇 Resources > JDBC Providers。
單擊 New 按鈕。
對於 JDBC Providers,請選擇 DB2 Universal JDBC Driver Provider。
單擊 OK。
您可以將 New provider definition 頁上的所有內容保留原樣。
單擊 OK。
單擊該頁頂部的 Save 鏈接並保存設置。
定義我們的 DB2 JDBC 驅動程序的位置:
選擇 Environment > Manage WebSphere Variables。
將以下環境變量設置為 db2java.zip 文件的位置。如果您沒有安裝 DB2 或 DB2client,則需要從已 安裝的 DB2 部署獲得該文件,並將其放在您系統上的某個地方。 ${DB2UNIVERSAL_JDBC_DRIVER_PATH}
${UNIVERSAL_JDBC_DRIVER_PATH}
對於每個變量:
單擊變量名稱。
對於 Value 字段,輸入 db2java.zip 文件所在的目錄。對於 Linux: /opt/IBM/db2/V8.1/java.
單擊 OK。
單擊該頁頂部的 Save 鏈接並保存設置。
創建 J2C 身份驗證別名:
選擇 Security > JAAS Configuration > J2C Authentication Data。
單擊 New 按鈕。
輸入以下字段,並根據您實現的需要進行調整:
Alias:sampleDBAuth
User ID:您的 DB2 實例的用戶 ID,例如 db2inst1
Password:您的 DB2 實例的密碼
單擊 OK。
單擊該頁頂部的 Save 鏈接並保存設置。
為我們的示例數據庫創建一個新的數據源。
選擇 Resources > JDBC Providers。
單擊我們剛才創建的 JDBC 提供程序 DB2 Universal JDBC Driver Provider。
單擊頁面頂部的 Data Sources 鏈接。
單擊 New 按鈕。
設置 Configure Data Source 頁上的以下字段:
Name:sample
JNDI name:jdbc/sample
Container-Managed Authentication Alias:xxxxx/sampleDBAuth
注意:該值將附帶您的應用程序服務器節點名稱作為前綴。
單擊 OK。
單擊新創建的示例數據源的鏈接。
單擊頁面底部的 custom properties 鏈接。
對於下面所示的每個變量名稱,根據您的配置將各個值設置為適當的值。下面所示的值針對缺省安裝 。
serverName:localhost
portNumber:50001
更新各個字段以後,單擊屏幕頂部的 Save 鏈接並保存設置。
測試新的數據源:
選擇 Resources > JDBC Providers。
單擊我們剛才創建的 JDBC 提供程序 DB2 Universal JDBC Driver Provider。
單擊頁面頂部的 Data Sources 鏈接。
單擊新創建的示例數據源的鏈接。
單擊 Test Connection 按鈕。您應該在屏幕頂部看到成功消息。如果看到錯誤,請檢查所有設置並 重試。另外,還要確保實際將數據庫運行起來!
祝賀您,現在您的數據庫和數據源已經准備就緒,可供使用了。
附錄 B. 部署 Portlet
本部分提供相關信息,介紹如何使用 RAD,通過幾次簡單點擊來完成 Portlet WAR 文件的部署。
在 Project Explorer 中,右鍵單擊 Ajax_and Portal project 並從菜單中選擇 Deploy Portlet...,如圖 12 所示。
圖 12. Deploy Portlet 菜單選項
配置一個門戶服務器以支持部署。當您第一次運行此向導時,將不存在已配置的服務器。如果沒有已 配置的服務器,則按照以下步驟操作,否則繼續步驟 3。
單擊 New... 按鈕以配置新服務器。注意:如果您希望將 Portlet 部署到遠程門戶服務器,則您應 該擁有 FTP 訪問權限或某個共享目錄(例如,NFS)。設置遠程服務器的主題超出了本練習的范圍,您 需要參考 RAD 和 WebSphere Portal 幫助文檔。
對於普通門戶環境,您需要選擇如圖 13 所示的值。請調整這些值以匹配您的環境並單擊 Next。
圖 13. 部署和定義新服務器
輸入以下信息:圖 14 顯示了標准值,不過您可能必須根據特定的部署進行調整。當完成時,單擊 Next。
圖 14. 部署門戶設置
配置發布設置。圖 15 所示的值針對一個 Linux 平台上的本地安裝。請調整這些值以匹配您的配置 ,然後單擊 Finish。
圖 15. 部署發布設置
選擇您希望將 Portlet 部署到的適當服務器。通常,該服務器是 localhost > WebSphere Portal v5.1 for Import, Export and Deploy @ localhost。選擇服務器之後,單擊 Finish。請參見 圖 16。
如果以前已部署了該 Portlet,您將看到一個彈出窗口,提示您是否改寫現有內容。回答 Yes 以繼 續部署所做的更改。
圖 16. 部署服務器選擇
現在您的 Portlet 應該已部署到該服務器上!
如果這是第一次部署該 Portlet,您需要將其添加到門戶上的某個頁面:
使用您的 Web 浏覽器,使用管理帳戶登錄門戶。
[可選] 為該 Portlet 創建一個新頁面。
選擇 portal Administration 鏈接。
選擇 Portal User Interface > Manage Pages。
從列表頁中選擇 My Portal 鏈接。
單擊 New page 按鈕。
提供 Ajax 和 Portal 名稱,並根據情況進行自定義。單擊 OK ,然後再次單擊 OK。
將該 Portlet 添加到某個頁面:
選擇 portal Administration 鏈接。
選擇 Portal User Interface > Manage Pages。
從列表頁中選擇 My Portal 鏈接。
單擊您將放置 Portlet 的頁面所對應的 Edit Page Layout 按鈕。
單擊 Add portlets 按鈕。
搜索“Ajax”,選擇 Ajax Database Explorer Portlet,並單擊 OK。
單擊 Done 以完成頁面編輯。
結束語
在本教程中,我們描述了如何和為什麼在門戶應用程序中使用 Ajax。我們還構建了一個在多個區域 使用 Ajax 的全功能 Portlet 應用程序。我們還向您介紹了 DHTML 操作所涉及到的緊密耦合技術。雖 然無法在本教程中研究 Ajax 和 WebSphere Portal 的所有可能用途,但我們的意圖旨在促使您學習並 在將來的應用程序開發工具包中應用這項激動人心的技術。
本文配套源碼