從屬列表問題(dependent list problem)"的問題時常被提出。問題時常出現於當你有兩個以上的選擇列表時,一個主列表有若干個選項,你希望當用戶選擇主列表中的某個選項時,在其他的從屬列表中顯示相關的選項。你可以通過eXtensible Markup Language(XML)的數據島(data islands)來實現這一功能,把XML內嵌到你的Html中。這一結果對在客戶端的XML應用具有實踐意義。
假設你為一家名為"CheapPc"的公司工作,你負責讓訪問公司網站的用戶找到用戶所在州的"CheapPc"商店。在公司網站上你列出了州名的列表。當一個顧客在主列表中點擊選擇一個州時,你希望在從屬列表中顯示相關的商店列表。
問題在於:你怎樣填充從屬列表而不用刷新該頁面或不用每次用戶在主列表中選擇時都向服務器發送消息?XML數據島完美地解決這一問題。當用戶發出請求,所有商店的數據都連接成一個XML字符串(Listing 1)。返回的結果是一個鑲嵌在Html頁面中的數據島。該數據島使用以下XML結構:
1
Birmingham
2
Huntsville
...etc.
根元素包含州名的列表,每個州名的列表依次包含商店的列表。每個商店都有一個相應的ID和城市名。為了生成這一XML字符串,你得用ActiveX Data Object(ADO)查詢數據庫,並用XML標記符格式化查詢結果。把記錄集數據格式化成Html表格和把記錄集數據格式化成XM文件之間有一些不同。在本文的後半部分,我將向你展示如何避免過多的串聯工作以提高響應時間。你可以點擊這裡下載例程。把處理過程移到客戶端 當Web服務器返回網頁後,所有進一步的操作處理都在客戶端進行。我原先認為如果向客戶端發送一個大的數據集會使程序的速度放慢。但後來我卻發現一次性發送所有數據比在客戶與服務器之間來回地進行數據交換快得多。唯一可被注意到的停頓只是在IE5的XML解析器讀取數據時的一個短暫的初始化停頓。 Html文件本身包含有4個元素:一個保存州名數據的選擇列表(主列表),XML數據,一個從屬列表的div標記占位符,和兩個腳本。當州名選擇列表的選項發生變化時,第二個腳本就執行(Listing 2)。 你希望每次用戶選擇一個州時都能從數據島重新得到相應的商店列表。所以你需要一個腳本去處理州名選擇列表的onChange事件:
在這個腳本中重新得到了州名選擇列表的值: Dim stateCode stateCode=document.all.State.value 接著重新得到所選定州的商店列表。你可以通過兩個方法得到這個列表。第一個辦法就是獲得<XML>標記符的記錄集屬性的引用: Dim R Set R = XMLdata.recordset 通過這個方法得到的記錄集是一個標准的ADO記錄集對象,字段名都與標記符相對應。這個方法只有在每個記錄都有同樣的字段的"簡單"XML數據文件中才能很好地工作。 第二個方法就比較強大和普遍。IE5把XML數據島象其他Html元素一樣對待。為了在客戶端訪問XML數據,你只需通過ID來引用該元素。IE5將返回XML樹的根元素(document元素): set xml = document.all("XMLdata") 你用這個document元素可以執行Extensible Stylesheet Language (XSL)查詢語句。使用XSL查詢語句,你可以得到某個給定層次上的節點列表,包含指定屬性的節點列表,或以上兩者的任意組合。例如,以下的查詢語句返回所有符合用戶所選州的商店節點: Dim nodes set nodes = XML.selectNodes ("locations/state[@code='" & stateCode & "']/store") XSL搜索字符串看起來很象一個文件路徑;它有XML樹相應各層次的值。用"/"分隔每個層次。以"節點=數值"的匹配方式加到相關節點的"[]"中搜索指定數值的節點,在屬性名前加上"@"來搜索包含指定屬性的節點。運行selectNodes方法將返回一個IXMLDOMNodes集合對象。最後,顯示從屬列表。最簡單的方法:根據XSL查詢語句返回的節點列表來改變從屬列表的標記符的值: set aList = document.all("citIEs") aList.options.length = 0 i = 0 For Each N In nodes anID = N.selectSingleNode("id").text aCity = N.selectSingleNode _("city").text aList.options.length = _aList.options.length + 1 aList.options(i).id = anID aList.options(i).value = aCity aList.options(i).innerText = aCity i = i + 1 Next 現在你知道了如何利用XML數據島來解決從屬列表問題。注意:這一技巧不適合於大規模的數據-如那些成千上萬的記錄-但如果那樣的話,那主要還是一個如何顯示的問題,而非技術上的問題。如果你發現一個查詢語句可能返回不適合的大組記錄,你即可以限制返回記錄的數目(例如為用戶提供一個"More Record"按鈕),又可以修改用戶界面使用戶可以點擊下一層次的內容,因此而限制了用戶必須選擇的記錄數目。 更多內容請看XML詳解 開發應用專題,或 把數據庫的表數據保存到文件中 我曾答應向你展示如何避免每次請求都要讀取數據庫和串聯成XML字符串的步驟。答案就是把數據庫的表數據保存到文件中;這樣做有幾個好處。讀取文件比查詢數據庫並把記錄集格式化成XML要快。這樣做也可以簡化你的HTML文件,因為你只需在Html中包含對XML的引用。最後這樣做使數據對於客戶端來說是透明的。如果你以Listing 1中的方式發送XML標記符的話,用戶可以通過IE的"查看/源文件"看到數據。使用文件引用的話,用戶只能看到引用標記符,而非數據。 只要你的數據庫記錄不是永不改變的,你就還得在數據改變時查詢數據庫。例如,你可以在SQL Server中寫一個觸發器,當"Location"表發生改變時更新"LastChanged"表中的日期和時間(Listing 3)。對於每一次請求,你可以比較Location"表與保存數據的XML文件的日期和時間。本文使用的Access數據庫不支持觸發器,但你可以編寫代碼來實現當"Location"表發生改變時更新"LastChanged"表的功能。你也可以每隔一段時間,如一天或一個小時就重新生成文件。 用Scripting.FileSystemObject 和 Scripting.TextStream對象寫XML文件到硬盤中。為了能寫XML文件,你必須給IUSR_MacHINENAME匿名用帳戶足夠的權限以打開、讀、寫和刪除文件(如果你使用的是NTFS格式的分區硬盤的話)。如果你只想利用XML數據重新在客戶端建立一個記錄集的話,你可以用adPersistXML方法保存記錄集到硬盤中。如果你這樣做的話,必須注意ADO為了能從XML數據重新建立記錄集而把記錄集保存成優化的格式。這樣生成的XML對於XSL查詢語句而言即不美觀又不方便: Dim fs Dim ts dim aFilename aFilename = Server.MapPath _("/XMLDataIslands") & _"\locations.XML" set fs = Server.CreateObject _("Scripting.FileSystemObject") set ts = fs.OpenTextFile _(aFilename, ForWriting) ts.Write s 現在,當你收到一個請求時,查看"LastChanged"表中日期和時間: Dim SQL Dim R Dim conn dim tableModifyDate set conn = Server.CreateObject _("ADODB.Connection") conn.mode = adModeRead conn.open "DSN=CheapPC;UID=sa;PWD=" SQL = "SELECT " & _"LastChanged.LastChanged " & _"FROM Locations WHERE " & _"Tablename='Locations'" set R = conn.execute(SQL,,adCmdText) tableModifyDate = R("LastChanged") 接下來,通過獲取一個File對象來比較XML文件與tableModifyDate的日期和時間,並查看它的FileCreatedOn屬性值: dim fs dim aFile dim rewriteFile rewriteFile=False set fs = Server.CreateObject _("Scripting.FileSystemObject") if fs.FileExists(aFilename) then Set aFile = fs.GetFile(aFilename) If aFile.DateCreated < _LocationsModifIEdDate Then rewriteFile=True end if else rewriteFile=true end if 如果XML文件不存在或需要更新時,你必須寫文件(Listing 4)。這樣就重新從數據庫讀取數據並格式化成XML,但你只需覆蓋舊的XML文件而不必把XML發送到浏覽器。現在在你的Html文件中,你只需引用該XML文件即可。當你的XML文件不需要更新,你的服務器會略過數據的讀取與格式化,只是簡單地讀取XML數據到浏覽器。從整體上看,讀取XML數據為你的服務器節省了大量的處理能力,使你的頁面更有效率。