nguoitapviet.info

VÍ DỤ SỬ DỤNG AJAX


Ví dụ này được viết sử dụng PHP và cơ sở dữ liệu MySQL bởi Trần Lê Duy Tiên. Các bạn có thể tải về tham khảo để áp dụng cho các hệ thống web của mình. Bài viết phân tích về kỹ thuật Ajax (Asynchronous JavaScript + XML) có thể tìm đọc tại nguoitapviet.info.

Các bạn vui lòng không tự phân phát lại mã nguồn của ví dụ này trên các trang web riêng của mình. Mọi thắc mắc về kỹ thuật có thể liên hệ với tớ qua nguoitapviet.info

Tổng quan

Ví dụ bao gồm 4 file: index.php (giao diện chính), process.php (nơi xử lý các yêu cầu và trả kết quả về dưới dạng XML), scripts.js (chứa các mã javascript), styles.css (định nghĩa giao diện)

Trước khi tìm hiểu sâu vào mã nguồn, trước hết hãy có cái nhìn tổng quan về những gì chúng ta muốn: Chúng ta muốn có một cách nào đó có thể gửi dữ liệu về trang chủ yêu cầu kiểm tra thông tin hoặc thực hiện một tác vụ nào đó và nhận kết quả về mà không cần phải rời trang hiện tại.

Cách chúng ta sẽ thực hiện là sử dụng javascript như một cơ chế nền phía sau giao diện thực hiện việc gửi và nhận kết quả. Hãy cứ tưởng tượng bạn là người phục vụ ở một nhà hàng. Bạn lấy thông tin yêu cầu từ khách hàng và sau đó gửi cho một người chạy bàn (javascript) chuyển các yêu cầu này xuống bếp (máy chủ). Sau khi nhà bếp chuẩn bị xong món ăn, người chạy bàn sẽ chuyển món ăn lên cho bạn để bạn phục vụ khách. Như vậy, bạn sẽ không cần phải rời khỏi khu phục vụ.

Hãy bắt đầu tìm hiểu nhà bếp trước.

process.php - "Nhà bếp"

Bên trong file process.php chỉ đơn thuần chứa các phương thức (hàm) thực hiện các tác vụ kiểm tra, truy vấn cơ sở dữ liệu và trả kết quả về dưới dạng XML. Nội dung các hàm trong ví dụ này hoàn toàn không phức tạp (chỉ là vài câu truy vấn cơ sở dữ liệu), nhưng chú ý là bạn sẽ trả về kết quả dưới dạng XML chứ không phải trang web bình thường.

Cấu trúc của kết quả XML trả về sẽ như sau:

<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
<response>
    <method> method_name </method>
    <result> result_value </result>
</response>

Phương thức generateXMLResult($method_name,$value) sẽ thực hiện việc in kết quả ra dưới dạng XML như trên. Chú ý nữa là ngay dòng đầu tiên của file process.php, bạn cần chỉ rõ phần header.

scripts.js - người bưng đồ và gửi yêu cầu

Đây là phần quan trọng nhất trong kỹ thuật Ajax: sử dụng javascript thông qua đối tượng XMLHttpRequest để gửi các yêu cầu về máy chủ trong chế độ nền.

Trước hết là việc tạo ra đối tượng XMLHttpRequest. Một điểm phức tạp là trình duyệt IE và Mozilla hỗ trợ XMLHttpRequest theo hai phương thức hoàn toàn khác nhau (IE sử dụng ActiveX, còn Mozilla thì hỗ trợ sẵn ngay bên trong trình duyệt) nên để đảm bảo tính tương thích, bạn sẽ cần phải kiểm tra xem người dùng sử dụng trình duyệt nào rồi khởi tạo đối tượng XMLHttpRequest theo cách thức mà trình duyệt đó hỗ trợ.

var req;

function loadXMLDoc(url)
{
  // branch for native XMLHttpRequest object
  if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
    req.onreadystatechange = processReqChange;
    req.open("GET", url, true);
    req.send(null);
  // branch for IE/Windows ActiveX version
  } else if (window.ActiveXObject) {
    req = new     ActiveXObject("Microsoft.XMLHTTP");
    if (req) {
      req.onreadystatechange = processReqChange;
      req.open("GET", url, true);
      req.send();
    }
  }

}

Như vậy, mỗi khi bạn muốn gửi một yêu cầu nào đó lên máy chủ, bạn chỉ cần chuẩn bị địa chỉ bạn muốn gửi yêu cầu đến cùng với các thông tin và sau đó gọi phương thức loadXMLDoc(url). Để ý dòng lệnh:

req.onreadystatechange = processReqChange;

Ý nghĩa của dòng lệnh này là thông báo cho javascript biết một khi đã nhận được bất kỳ phản hồi nào từ máy chủ về yêu cầu này thì sẽ gọi hàm processReqChange. Cần biết là quá trình gửi yêu cầu và nhận yêu cầu được chia làm nhiều giai đoạn (state), từ 0 (mới bắt đầu gửi) cho đến 4 (hoàn tất việc nhận kết quả). Bất kể ở giai đoạn nào, cứ mỗi khi có sự thay đổi là hàm processReqChange sẽ được gọi để kiểm tra những gì nhận được.

Vậy processReqChange sẽ thực hiện các kiểm tra gì?

function processReqChange()
{
  if (req.readyState == 4)
  {
    if (req.status == 200)
    {
      response=req.responseXML.documentElement;
      A_Method=response.getElementsByTagName('method')[0].firstChild.data;
      A_Result=response.getElementsByTagName('result')[0].firstChild.data;
      if (A_Method!='addNew')
      {
        eval(A_Method + '(\'\', A_Result)');
      }
      else
      {
        eval(A_Method + '(\'\',\'\', A_Result)');
      }
    }
    else
    {
      alert("Problem retrieving the XML data:\n"+req.statusText);
    }
  }
}

Đầu tiên, phương thức kiểm tra xem liệu đã nhận được thông tin cuối cùng từ máy chủa chưa (readyState == 4). Nhưng không chỉđơn giản vậy, kế tiếp, kiểm tra xem máy chủ gửi về thông báo gì. Nếu không có vấn đề gì thì máy chủ sẽ trả về dữ liệu kết quả dưới dạng XML như chúng ta thiết kế trong process.php - tuy nhiên còn rất nhiều các khả năng khác như quá thời hạn (time-out), file không tìm thấy (lỗi 404-file not found),... Vậy nên chúng ta cần kiểm tra trước và chỉ tiếp tục nếu nhận được đúng dữ liệu chúng ta muốn (status=200).

Một khi nhận được dữ liệu ở dạng XML (đối tượng XML được khởi tạo bằng cách sử dụng thuộc tính responseXML có sẵn của đối tượng XMLHttpRequest), chúng ta cần phân tích nội dung của file XML đó để lấy thông tin kết quả xử lý. Ở đây, ví dụ này sử dụng DOM (Document Object Model) để phân tích. Tớ không thể giải thích chi tiết cách thức sử dụng công nghệ DOM trong bài này - các bạn hoặc là tạm thời chấp nhận mã trên (thật ra cũng dễ đoán) hoặc có thể tìm hiểu về DOM tại đây.

Chúng ta sẽ phân tích từ nội dung XML chuẩn bị bởi process.php để lấy: 1) tên của phương thức gửi yêu cầu đến máy chủ trước đó, và 2) kết quả xử lý yêu cầu đó. Chúng ta cần biết tên phương thức gọi yêu cầu để có thể gọi lại và yêu cầu thực hiện các thay đổi trên giao diện (cũng giống như người phục vụ sẽ cần phải biết được món nào của bàn nào sau khi nhận món ăn từ người bưng đồ).

Cuối cùng, hãy thử phân tích xem chúng ta sẽ thực hiện việc gửi yêu cầu, nhận yêu cầu và thực hiện các thay đổi như thế nào qua một ví dụ.

Áp dụng

Hãy lấy ví dụ đơn giản nhất là kiểm tra thử tên người dùng đã có trong cơ sở dữ liệu chưa (xem thử ví dụ trước). Việc kiểm tra sẽ được thực hiện ngay mỗi khi người dùng nhập xong tên và chuyển sang ô khác (nhấn chuột ra ngoài hay nhấn "tab" trên bàn phím) bằng cách sử dụng phương thức "onblur":

<input name="username" type="text" onblur="checkName(this.value,'')" />

Như ở trên, mỗi khi người dùng chuyển sang ô khác, phương thức checkName sẽ được gọi kèm theo nội dung mà người dùng mới nhập vào để gửi đi kiểm tra:

function checkName(input, response)
{
  if (response != '')
  {
    message = document.getElementById('nameCheckFailed');
    if (response == 0){
      document.getElementById("name").focus();
      message.className = 'error';
      document.getElementById("name").className="textForm_Error";
      name_OK = false;
    } else {
      message.className = 'hidden';
      document.getElementById("name").className="textForm_Accept";
      name_OK = true;
    }
    checkReady();
  }
  else
  {
    if(input!='')
    {
      url = 'http://localhost/ajax example/process.php?task=1&q='+input;
      loadXMLDoc(url);
    }
    else
    {
      name_OK = false;
    }
  }
}

Phương thức checkName sẽ thực hiện 2 nhiệm vụ: nếu như response!="", tức là nhận được phản hồi từ máy chủ, thì nó sẽ thực hiện các thay đổi trên giao diện. Ngược lại có nghĩa là chưa có yêu cầu hoặc phàn hổi nào cả thì nó sẽ thực hiện việc gửi yêu cầu (để ý cách chuẩn bị nội dung gửi lên qua phương thức GET trong url). Đây cũng chính là lí do tại sao chúng ta cần phải biết tên phương thức gửi yêu cầu trong hàm processReqChange để có thể yêu cầu lại chính phương thức đó thực hiện các thay đổi trên giao diện mà nó muốn.

Trong file scripts.js còn rất nhiều các phương thức tương tự (như checkEmail, addNew,...) có cách họat động hoàn toàn giống phương thức trên.

Tải về mã nguồn

Mã nguồn của toàn bộ ví dụ bạn thấy có thể tải về tại đây. Tớ có kèm theo file sql để các bạn có thể import vào cơ sỡ dữ liệu mysql của mình.