2010年8月5日 星期四

[AJAX]-動態清單

這裡要記錄如何用Ajax來達到動態清單的功能
Ajax 是 Asynchronous JavaScript  and XML 的簡稱,這指出了 Ajax 的核心觀念
(Asynchronous)與所使用到的主要兩個技術(JavaScript、XML)。
要達到 Asynchronous 利用的就是 JavaScript 中的XMLHttpRequest 物件。

XMLHttpRequest:
在 JavaScript 中利用new XMLHttpRequest()語法建立。
包含下列幾個標準屬性-

onreadystatechange 
參考至callback函式,readyState每次改變時,都會呼叫onreadystatechange所參考的函式。

readyState 
會有0到4的數值,分別表示不同的請求狀態:
 0 = 未初始化的連線(uninitialized),還沒呼叫open()
 1 = 載入中(loading),呼叫open(),還沒呼叫send()
 2 = 已載入(loaded),呼叫send(),請求header/status準備好
 3 = 互動中(interactive),正在與伺服器互動中
 4 = 請求完成(completed),完成請求

responseText 
伺服器傳來的請求回應文字,會設定給這個屬性。

responseXML
伺服器傳來的請求回應如果是XML,會成為DOM設定給這個屬性。

status 
伺服器回應的狀態碼,例如200是OK,404為Not Found…

statusText 
伺服器回應的狀態文字。
接下來我們實際寫個web頁面來發送XMLHttpRequest
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>動態載入清單</title>
<script type="text/javascript" src=dynamicList.js></script>
</head>
<body>
語言…
<br/>
<select id="langs" onchange="refreshList();">
<option value="func">功能說明</option>
<option value="java">Java</option>
<option value="c#">C#</option>
</select>
<br/><br/>
相關技術…
<br/>
<select id="techs" size="6" style="width:300px;"/>
</body>
</html>
主要功能是選擇 id 為 langs 的 select html 元件時,會利用 Ajax 去後端 server 取得
相關資料,非同步的更動 id 為 techs 的 select html元件。
另外主要功能的JavaScript是利用
<script type="text/javascript" src=dynamicList.js></script>來引入。
var xmlHttp;

window.onload = refreshList;

function createXMLHttpRequest() {
  if(window.XMLHttpRequest) {
    xmlHttp = new XMLHttpRequest();
  }
  else if(window.ActiveXObject) {
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
  }
}

function prepareXML() {
  var xml = "<langs>";
  var lang = document.getElementById("langs").value;
  xml = xml + "<lang>" + lang + "<\/lang>";
  xml = xml + "<\/langs>";
  return xml;
}

function refreshList() {
  var xml = prepareXML();
  var url = "refreshservlet";

  createXMLHttpRequest();
  xmlHttp.onreadystatechange = handleStateChange;
  xmlHttp.open("POST", url);
  xmlHttp.setRequestHeader("Content-Type", "text/xml");
  xmlHttp.send(xml);  
}

function handleStateChange() {
  if(xmlHttp.readyState == 4) {
    if(xmlHttp.status == 200) {
      clearList();
      updateList();
    }
  }
}

// 清除上一次的顯示結果
function clearList() { 
  var techs = document.getElementById("techs");
  while(techs.childNodes.length > 0) {
    techs.removeChild(techs.childNodes[0]);
  }
}

// 以回應更新資料
function updateList() {
  var results = xmlHttp.responseXML.getElementsByTagName("tech");
  var techs = document.getElementById("techs");

  var option = null;
  for(var i = 0; i < results.length; i++) {
    option = document.createElement("option");
    option.appendChild(document.createTextNode(results[i].firstChild.nodeValue));
    techs.appendChild(option);
  }
}
由上面我們可以看到將XMLHttpRequest的onreadystatechange屬性
註冊到handleStateChange method,並利用post機制傳送XML格式的資料。
而所謂的XML格式資料其實就是符合XML規格的字串資料。
因為非同步的資料皆由XMLHttpRequest所控制,所以server回傳資料
也是由 XMLHttpRequest 的 responseXML 屬性取得。

接下來我們用一個servlet來接收並回傳由client端發送來的資料。
public class RefreshServlet extends HttpServlet {
  private static Map inMemoryDB = new HashMap();

  public void init() throws ServletException {
    inMemoryDB.put("func", new String[] { "非同步", "動態清單" });
    inMemoryDB.put("java", 
    new String[] { "JSP", "J2EE", "Spring", "Hibernate" });
    inMemoryDB.put("c#", new String[] { "ASP.Net", "ADO.Net" });
  }

  protected void doPost(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException, IOException {
    String xml = readXMLFromRequestBody(request);
    Document xmlDoc = null;

    try {
      DocumentBuilder builder = 
      DocumentBuilderFactory.newInstance().newDocumentBuilder();
      xmlDoc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
    } catch (ParserConfigurationException e) {
      System.out.println(e);
    } catch (SAXException e) {
      System.out.println(e);
    }

    String responseXML = prepareXMLResponse(xmlDoc);

    response.setContentType("text/xml;charset=utf-8");
    response.getWriter().print(responseXML);
    response.getWriter().close();
  }

  private String readXMLFromRequestBody(HttpServletRequest request) {
    StringBuffer xml = new StringBuffer();

    try {
      BufferedReader reader = request.getReader();
      String line = null;
      while ((line = reader.readLine()) != null) {
        xml.append(line);
      }
    } catch (Exception e) {
      System.out.println("XML讀取有誤…" + e.toString());
    }
    return xml.toString();
  }

  private String prepareXMLResponse(Document xmlDoc) {
    NodeList langNodes = xmlDoc.getElementsByTagName("langs");
    String lang = langNodes.item(0).getFirstChild().getTextContent();

    StringBuffer xml = new StringBuffer();
    xml.append("<techs>");
    String[] techs = (String[])inMemoryDB.get(lang);
    for (int i = 0; i < techs.length; i++) {
      xml.append("<tech>");
      xml.append(techs[i]);
      xml.append("</tech>");
    }
    xml.append("</techs>");
    return xml.toString();
  }
}

因為XML也是字串資料,所以我們先用reader讀入
BufferedReader reader = request.getReader();
String line = null; 
while ((line = reader.readLine()) != null) {
     xml.append(line);
 }
接著利用 DocumentBuilder 來解析每個tag的資料
DocumentBuilder builder =
     DocumentBuilderFactory.newInstance().newDocumentBuilder(); 

xmlDoc = builder.parse(new ByteArrayInputStream(xml.getBytes()));

最後也是利用 StringBuffer 將查詢到的資料組成XML格式的字串資料回傳。

沒有留言:

張貼留言