Dùng Dojo và JSON xây dựng Ajax Applications


"Ajax" đã là một từ khá thông dụng và ngày càng hữu dụng. GmailGoogle Suggest là các trang viết và giới thiệu thật hoành tráng cho JavaScript bất đồng bộ (asynchronous). Lợi ích của người dùng và những yêu cầu càng tăng của họ.

Trong bài viết này, Tôi sẽ chỉ cách xây dựng một ứng dụng dùng DojoJSON—là 2 phần khác nhau nhưng bổ xung cho nhau thành một ứng dụng hoàn chỉnh। Chúng ta sẽ hiểu hơn về Dojo và JSON và xây dựng một Ajax applications. Bài viết này cũng sẽ cho bạn biết cách tạo một Dojo widgets bằng cách hiện thực tình năng "autocomplete" cho một text boxes. download



Đặt vấn đề


Ajax hiểu theo một cách ngắn gọn là xử lý trao đổi dữ liệu giữa web server và web browser dùng JavaScript khi trang đã được load. Trên thực tế, nó có nghĩa là chuyển đổi request trên browser, xử lý bất đồng bộ JavaScript, vậy xử lý request/response một cách trực tiếp đến user. Để hoàn thành nó, chúng ta có thể viết các đoạn code JavaScript hay dùng một libraries như Dojo.

Dojo

Dojo bao gồm nhiều chức năng đã được viết sẵn bằng JavaScript. Một trong những tính năng đó là tạo gởi HTTP requests và nhận responses. Và đây cũng là tính năng chính mà ta sẽ dùng. Về chức năng nó cung cấp về Ajax, Dojo cung cấp gói cho thao tác trên string, thao tác trên DOM, hỗ trợ kéo thả, và một số cấu trúc quen thuôc như lists, queues, và stacks.

JSON

JSON là một thư viện(library) Java giúp ta chuyển một đối tượng(object) Java sang biểu diễn dạng chuổi(string). Chuổi này khi eval()gọi trên JavaScript, nó sẽ tạo thành một mảng (array) chứa các thông tin về Java object. JSON là một kiểu đối tượng ghi chú, phù hợp cho mã hoá nhiều cấu trúc của đối tượng. Theo cách này ta sẽ tạo ra được một cấu trúc nhỏ hơn một file XML về thành phần, tiện lợi hơn khi dùng tính năng eval(), là một sự lựa chọn cho việc chuyển dữ liệu giữa browser và server.

JSON được xây dựng dùng cùng với JavaScript để tạo requests. Code ở server, có thể viết trên nhiều ngôn ngữ khác nhau, như C#, Python, PHP, và đương nhiên, Java!


Một HTTP Request đơn giản


Trong ví dụ đầu tiên, tôi cùng Dojo để cung cấp dòng chào mừng khi người dùng click lên trên một button. Minh hoạ cho việc tạo và gọi một Ajax dùng Dojo. Vào http://localhost:8080/dojo_json/example1.jsp và nhập tên, Submit. Một JavaScript thông báo hiển thị dòng chào mừng được tạo trên server. Bạn có thể xem source trên cho example1.jsp


<html>

<body onLoad="onLoad();">
<head>
<title>Example 1</title>
<script language="javascript" src="js/dojo.js"></script>
<script language="javascript">
dojo.require("dojo.io.*");
dojo.require("dojo.event.*");

function onLoad() {
var buttonObj = document.getElementById("myButton");
dojo.event.connect(buttonObj, "onclick",
this, "onclick_myButton");
}

function onclick_myButton() {
var bindArgs = {
url: "welcome.jsp",
error: function(type, data, evt){
alert("An error occurred.");
},
load: function(type, data, evt){
alert(data);
},
mimetype: "text/plain",
formNode: document.getElementById("myForm")
};
dojo.io.bind(bindArgs);
}
</script>
</head>
<body>
<form id="myForm">
<input type="text" name="name"/>
<input type="button" id="myButton" value="Submit"/>
</form>
</body>
</html>

Đầu tiên là import Dojo libraries, tên là dojo.js. Chứa tất cả class của Dojo cần cho phần lớn tính năng của nó. Dojo libraries được đóng gói như một Java code. Ví dụ, tôi cần import 2 gói. Gói dojo.io chứa các class cho phép tạo một HTTP requests dùng các giao thức như XMLHTTPTransport. Gói dojo.event cung cấp tập lệnh thao tác trên DOM và gán sự kiện.

Tiếp theo, tôi gán onclick cho button. Điều mà trước đây ta phải làm như sau:

<input type="submit" onclick="someFunc();"/>

nhưng với đoạn này thì ta chỉ gán cho sự kiện onclick một sử lý nhất định. Phương thức dojo.event.connect() cho phép chúng ta liên kết một xử lý cho nhiều HTML elements. Ở đây, tôi gán onclick_myButton là một xử lý cho myButton.

Khi onclick_myButton executes, tôi gọi đến welcome.jsp và nhận lại response. Dùng phương thức dojo.io.bind(). trong đó có đối số bindArgs, là một mảng name/value. Trong ví dụ trên tôi dùng 5 cặp:

  1. url: URL để gởi request đến.

  2. mimetype: Kiểu của response.

  3. load: Code gọi khi hoàn tất.

  4. error: Code gọi khi lỗi.

  5. formNode: ID của form nơi mà fields truyền tham số đến URL.

Tất cả các tham số bạn có thể tìm thấy ở đây.

Khi gọi dojo.io.bind(bindArgs), là bắt đầu quá trình xử lý, nó phụ thuộc vào request mà ta có thể nhận về errors,load hay error được gọi. Cả loaderror đều có 3 tham số:

  1. type: kiểu chức năng; luôn luôn cho loadload()errorerror().

  2. data: response nhận về. nếu mimetype thì khai báo như text/plain. Nếu text/json được dùng, dữ liệu sẽ có dạng eval('(' + dữ liệu nhận về + ')').

  3. evt: Sự kiện của đối tượng.

Tiếp theo, tôi có thể khai báo một sự kiện dùng dojo.event.connect() bằng cách tạo một chức năng handleResponse(type,data,evt) và kết hợp với request:

var req = dojo.io.bind(bindArgs); dojo.event.connect(req, "load", this, "handleResponse");

Trường hợp này, tôi không cần khai báo thuộc tính load trong bindArgs.

Chú ý: Vì lý do bảo mật, Firefox không cho phép giá trị cho url trong bindArgs liên kết đến một URL trên một domain khác. Internet Explorer thì cho phép.


Dùng JSON chuyển đối tượng Java


Như đã nói ở trên, JSON giúp chuyển Java objects thành chuổi eval()của JavaScript. Trong ví dụ này, người dùng sẽ nhìn thấy danh sách các quayển sách. Khi người dùng rê chuột qua tên sách, chúng ta sẽ tìm và hiển thị tất cả những thông tin về quyển sách đó dùng Ajax. Ví dụ 2, http://localhost:8080/dojo_json/example2.jsp, minh họa cho điều này.

Tạo và Cung cấp Java Objects

Class Book có 4 trường, BookManager có phương thức trả về một object Book object tuỳ thuộc vào bookId và một phương thức trả về tất cả sách có. Trong ví dụ này, tôi đã code cứng 5 quyển sách trong BookManager.

Phương thức Book.toJSONString() chịu trách nhiệm chuyển object Book thành JavaScript eval()thành String dùng trong JSON API.

public String toJSONString() throws JSONException {

JSONObject jsonObj = new JSONObject();
jsonObj.put("bookId", new Integer(this.bookId));
jsonObj.put("title", this.title);
jsonObj.put("isbn", this.isbn);
jsonObj.put("author", this.author);
return jsonObj.toString();
}

Class JSONObject chuyển object Book thành string bên trong dấu ngoặc, với dấu 2 chấm giữ tên và giá trị, và dấu ngoặc kép giựã giá trị và tên của trường khác, phù hợp String với eval()JavaScript. Còn class JSONArray có thể chuyển một danh sách Java objects sang JavaScript arrays.

Chuyển và điều hướng JSON-Encoded Data

Phụ thuộc vào bookId, book.jsp trả về một Book object trên form của JSON. Trang JSP tìm kiếm bookId dùng phương thức BookManager.getBook(bookId) và in ra kết quả qua phương thức Book.toJSONString(). Vì vậy http://localhost:8080/dojo_json/book.jsp?bookId=1 sẽ in ra JSON string cho Book với giá trị bookId1.

Trở lại tầng client, thông tin book được hiển thị trên list. Mổi thành phần trên danh sách có mouseover (trMouseOver) và mouseout (trMouseOut) sự kiện được truyền vào . Mỗi lần sự kiên mouseover, ta gọi book.jsp và truyền vào bookId. Kết quả trả về là một JSON String của quyển sách này Book. Tôi khai báo mime-type như một text/json vì vậy, giá trị trả về sẽ được tự động gọi eval(). Nhìn vào đoạn code JavaScript chịu trách nhiệm gởi request và xử lý response:

function trMouseOver(bookId) {

getBookInfo(bookId);
}

function trMouseOut(evt) {
var bookDiv = document.getElementById("bookInfo");
bookDiv.style.display = "none";
}

function getBookInfo(bookId) {
var params = new Array();
params['bookId'] = bookId;
var bindArgs = {
url: "book.jsp",
error: function(type, data, evt){
alert("error");
},
mimetype: "text/json",
content: params
};
var req = dojo.io.bind(bindArgs);
dojo.event.connect(req, "load", this, "populateDiv");
}

function populateDiv(type, data, evt) {
var bookDiv = document.getElementById("bookInfo");
if (!data) {
bookDiv.style.display = "none";
} else {
bookDiv.innerHTML = "ISBN: " + data.isbn +
"
Author: " + data.author;
bookDiv.style.display = "";
}
}

Trong một số ví dụ khác dùng thuộc tính content của bindArgs thay cho formNode. Thuộc tính content là một key/value liên kết đến thuộc tính được khai báo mapping of properties to be constructed into parameters passed with the data request.

Khi dữ liệu truyền vào JavaScript object, tôi có thể dể dàng truy cập đến các thuộc tính của Book và hiển thị đến người dùng trong

element, như bạn thấy.

Mở rộng Dojo: Tạo Autocompletion Widget


Trong 2 ví dụ trên, ta đã tìm hiểu các thành phần chính của một ứng dụng Ajax: chuyển đổi dữ liệu với web server dùng JavaScript. Một cách hiểu đơn giản, nó mang đến cho developer sử dụng một cách hiệu quả UIs dùng Ajax tools như Dojo. Dojo là một framework có thể mở rộng cho widget và phát triển module sẽ dễ dàng hơn. Trong sự lựa chọn này, tôi sẽ tạo một widget cho phép người dùng tạo một autocompletion cho một trường trên form.

Dojo Widget

Nói theo cách đơn giản, widget là tên của UI element như button, text box, scroll bar, v.v.v... Khi tạo Dojo widget, ngoài những thẻ HTML chúng ta còn có thể tạo một widgets bao gồm nhiều thành phần input, với của ta styles, và nhiều thứ khác. Bạn cần định nghĩa một trình điều khiển sự kiện cho widgets, tạo hành vi, và có thể dùng lại khi tạo một widget khác. Trong ví dụ này, tôi sẽ tạo một widget cung cấp tính năng autocompletion cho text box của HTML form.

Thành phần của Widget

Khi phát triển widgets, chúng ta cần quyết định UI elements nào và làm như thế nào. Khi dùng Dojo, có nghĩa là dùng HTML elements và viết code JavaScript để định nghĩa ứng sử. Ví dụ autocomplete dùng 4 files:

  • dojo.js: Tất cả Dojo libraries.

  • js/dojo/utils/templates/AutoComplete.html: Chứa UI elements, bao gồm HTML tags như

    <input>. File này có thể chứa nhiều đoạn HTML và giới hạn bởi một root element. Nếu nhiều hơn một element tìm thấy trên cùng, thì element đầu tiên sẽ là root.

  • js/dojo/utils/AutoComplete.js: JavaScript code cho widget. Thường là class mở rộng của một trong các Dojo Widget classes.

  • example3.jsp: File mà widget được dùng. Thường là thẻ được định nghĩa trong widget.

Học và hiểu làm sao mà UI elements có thể quản lý và thao tác bằng JavaScript là điều quan trọng trong tạo widgets.

Quản lý UI Elements

Như đã nói trên, chỉ định element gốc. Trong ví dụ autocomplete, element gốc là một

. Element này có thể quản lý torng class AutoComplete dùng this.domNode. Biến this.domNode lưu một liên kết đấn đối tượng HTML, xem ví dụ sau:

this.domNode.style.display = "none";

Để quán lý các element của UI từ JavaScript, bạn có thể dùng this.domNode cho đến khi bạn tìm ra , hay bạn có thể dùng thuộc tính dojoAttachPoint.

<input type="text" name="someTextField" dojoAttachPoint="myTextField"/>

Nếu bạn đã định nghĩa trong file HTML, nó có thể dễ dàng quản lý dùng this.myTextField trong JavaScript. Giống như this.domNode, this.myTextField cũng liên kết đến một đối tượng HTML.

Với Dojo, bạn có thể dễ dàng gắn nhiều sự kiện cho UI elements, kể cả dùng JavaScript và trường của element. Như đã minh hoạ, thật dễ dàng gắn sự kiên cho element, tôi dùng dojo.event.connect():

dojo.event.connect(this.domNode, "onclick", this, "myFunction")

Đoạn trên sẽ gắn onclick sự kiện myFunction() từ class này đến element gốc của UI. Tiếp theo, tôi có thể chỉ định HTML code cho thẻ

<div>:

<div dojoAttachEvent="onclick: myFunction">

<div dojoAttachEvent="onclick, onmouseover: myFunction">

Lập trình Widget

Giờ đã có cách quản lý và điểu khiển UI và các kiểu hành động của người dùng, và sau đó viết code JavaScript để tạo một hành vi ứng sử như ta mong muốn. Mỗi widget tương ứng với một class để quản lý UI elements và chịu trách nhiệm phản ứng với actions người dùng. Đoạn code Javascript sau được trích ra từ class AutoComplete sẽ minh hoạ cho một bộ widget.

dojo.provide("utils.AutoComplete");  dojo.require("dojo.dom"); dojo.provide("utils.AutoComplete");


dojo.require("dojo.dom");
...

dojo.widget.tags.addParseTreeHandler("dojo:AutoComplete");

utils.AutoComplete = function() {

// call super constructor
dojo.widget.HtmlWidget.call(this);

// load template
this.templatePath =
dojo.uri.dojoUri("utils/templates/AutoComplete.html");

this.widgetType = "AutoComplete";

// Instance variables
this.action = "";
this.formId = "";
this.form = {};
...

this.postCreate = function() {
this.form = document.getElementById(this.formId);
this.textbox = document.getElementById(this.textboxId);
dojo.event.connect(this.textbox, "onkeyup",
this, "textboxOnKeyUp");
...
}

this.textboxOnKeyUp = function(evt) {
if (this.isKey(evt.keyCode, 38, 40)) {
this.checkUpDownArrows(evt.keyCode);
} else {
bindArgs = {
url: this.action,
mimetype: "text/javascript",
formNode: this.form
};
var req = dojo.io.bind(bindArgs);
dojo.event.connect(req, "load", this, "populateChoices");
}
}

// Handler for "load" action of dojo.io.bind() call.
this.populateChoices = function(type, data, evt) {
...
}
}

// define inheritance
dj.inherits(utils.AutoComplete, dojo.widget.HtmlWidget);

Tôi đã định nghĩa một class JavaScript, AutoComplete, đoạn code ứng sử cho widget. Khi định nghĩa một widget, Dojo yêu cầu người phát triển khai báo một class dùng chức năng dojo.provide(). Đoạn inheritance này cho biết dùng chức năng dj.inherits(), và thực hiện thông qua việc gọi dojo.widget.HtmlWidget.call(this). File mẫu cho widget được load bằng cách gán giá trị cho biến this.templatePath cho class cha.

Dojo widgets dùng trang HTML dùng custom tags. Tags có thể định nghĩa khi viết một class liên kết. Trong ví dụ trên, bộ thể xuất ra là dojo:AutoComplete, được dùng trong file HTML trong đoạn code sau:

  action="getBooks.jsp"  textboxId="bookName"  formId="bookForm"/>

Đoạn output chứa đoạn template AutoComplete.html trên browser. Khai báo thuộc tính trong thẻ có thể quản lý dùng this. trong class JavaScript.

Khi người dùng nhập, nó sẽ tự động tìm ra danh sách những giá trị hợp lệ. Trong khi nhập, textboxOnKeyUp được gọi, và truyền đến web server cùng với giá trị nhập. Web server trả về một danh sách giá trị để hiển thị cho người dùng chọn. Tôi hiển thị nó trên thẻ <div>

.

Các đoạn code được tôi lược bớt là các đoạn thao tác trên thẻ <div>

, choices. Tìm ra phím tác động của người dùng, như Tab để chuyển sang trường khác để thao tác.

Kết luận


Dojo là một lựa chọn tốt khi phát triển Ajax trên một ứng dụng đòi hỏi JavaScript-intensive। Tuy nhiên, còn một số sản phẩm, như Scriptaculous và AjaxAnywhere, đem đến nhiều chức năng tuỳ theo yêu cầu của ứng dụng.

Hiendt (Java.net)



Cao Trong Hien

,

5 Responses to "Dùng Dojo và JSON xây dựng Ajax Applications"

ngovietphong said :
00:55 15 tháng 11, 2007
Kiến thức của mình về Java ít quá, nên đang học hỏi dần. Mình thấy Site của bạn là 1 nơi cung cấp kiến thức về Java khá dồi dào đấy.. Cố gắng phát huy nhé...
16:47 5 tháng 12, 2007
tôi đang xây dựng 1 website trên nền dojo Infomap. Nếu bạn có nhiều kinh nghiệm về dojo chúgn ta có thể chia sẻ với nhau.
10:03 6 tháng 12, 2007
Rất tiếc là mình không vô được trang đó nê n không biết nó ntn. Về kn dojo thì mình chỉ dùng một số tính năng của nó thôi, thực ra thì cũng vừa làm vừa học. Nếu bạn vui lòng thì mình cũng không ngại đâu, nói trước là mình chỉ làm với java thôi đó. :d
Nặc danh said :
13:48 26 tháng 8, 2008
You've used source code as if it's yours. Please, Comment in the rest of this topic the link that this source code belong to.

http://www.javapassion.com/handsonlabs/ajaxdojojson/
hiendt said :
18:56 26 tháng 8, 2008
Sorry,
All source code I've used and content I've commended that from Java.net.
But if that is your, and you allow, I'll recommend that from your, and so sorry about my mistake.

Best regards
hiendt

Đăng nhận xét