(Post 08/06/2007) Ajax ngày càng được sử dụng
rộng rãi, nó giúp xoá mờ khoảng cách giữa máy tính (desktop) và Internet.
Trong số báo trước TGVT đã có bài giới thiệu tổng quát về kỹ thuật lập
trình web sử dụng Ajax, tiếp tục chủ đề này, trong bài viết này tôi giới
thiệu một công cụ phát triển ứng dụng kiểu Ajax với ASP.NET.
Ajax.NET
Ajax.NET là thư viện Ajax cho .NET được phát triển bởi
tác giả người Đức tên là Michael
Schwarz, mục đích nhằm giúp lập trình viên .NET dễ dàng khai thác
sức mạnh của Ajax trong các ứng dụng web của mình. Thư viện này được cung
cấp miễn phí trên Internet, bạn có thể yêu cầu tác giả cho tải về cả mã
nguồn nếu muốn. Thư viện Ajax.NET hiện cũng được phát triển dưới một dự
án nguồn mở tên là BorgWorX Ajax.NET
với nhiều chức năng mở rộng. Ở đây tôi chỉ đề cập tới phiên bản Ajax.NET
đầu, sau khi làm quen với thư viện này bạn có thể dễ dàng khám phá phiên
bản mở rộng trong dự án nguồn mở trên, thậm chí tham gia dự án này.
Ajax.NET giúp lập trình viên sử dụng Ajax mà không phải
bận tâm đến mã lệnh tương tác trực tiếp với các đối tượng XMLHttpRequest,
xử lý dữ liệu XML, DOM. Bằng cách sử dụng những khả năng của môi trường
lập trình .NET như HttpHandler, reflection, attributes... nó tự động sinh
ra những mã lệnh cần thiết giúp việc tương tác giữa client/server hoàn
toàn "trong suốt" cũng như đơn giản hoá việc gọi hàm, xử lý
những dữ liệu phức tạp như mảng, bảng, đối tượng trên phía client.
Để hiểu rõ về cách hoạt động cũng như sử dụng Ajax.NET
trong một ứng dụng ASP.NET, tôi trình bày từng bước thực hiện một ứng
dụng minh hoạ đơn giản. Ứng dụng là một form đăng ký thành viên như chúng
ta thường gặp trên các diễn đàn, hoặc đăng ký tài khoản e-mail... Nguyên
tắc hoạt động của form là mỗi khi có thành viên mới đăng ký sẽ sử dụng
Ajax để kiểm tra xem tên (username) đã có chưa và hiển thị danh sách các
thành phố dựa trên quốc gia mà thành viên sinh sống. Các thay đổi sẽ thực
hiện ở "hậu trường" mà không phải thực hiện nạp lại toàn bộ
trang web hay lưu tạm một lượng lớn dữ liệu ở client như cách thông thường.
Bạn có thể tham khảo ứng dụng minh hoạ của bài viết này
tại đây, file
zip chứa toàn bộ mã nguồn của ứng dụng cũng có thể tải về từ địa chỉ trên.
Ứng dụng này được xây dựng với Visual Studio 2005 phiên bản CTP 09/2005,
sử dụng ngôn ngữ C#, tuy nhiên các kỹ thuật trình bày ở đây sẽ không có
gì khác biệt khi sử dụng với VB.NET hoặc Visual Studio .NET 2003.
Cài đặt ban đầu
Trước tiên cần tạo một website mới trong VS2005, sau
đó bạn tạo một tham chiếu (reference) tới thư viện Ajax.NET (file AjaxPro.dll
nếu sử dụng với VS.NET 2003, hoặc file AjaxPro.2.dll nếu là VS2005). Thư
viện Ajax.NET Professional sử dụng trong bài viết này là phiên bản mới
nhất 5.11.4.2, có thể tải
về từ đây.
Tiếp theo, cài đặt HttpHandler của Ajax.NET vào website
vừa tạo bằng cách thêm các dòng sau đây vào file Web.config (nếu sử dụng
VS2005 bạn phải tự thêm file này vào website).
type="AjaxPro.AjaxHandlerFactory, AjaxPro.2"/>
...
Các bước trên chỉ cần thực hiện một lần với một website,
sau đó chúng ta chuyển sang phần khai báo và viết các hàm xử lý trên phía
server.
Xây dựng các hàm Ajax phía server
Nhìn chung việc xây dựng các hàm để xử lý những yêu cầu
từ phía client không có gì khác so với các các quy định của .NET, ngoại
trừ một việc là những hàm này sẽ được "đánh dấu" bằng một thuộc
tính tên là AjaxMethodAttribute và có một số hạn chế sẽ được trình bày
ở phần sau. Dưới đây là khai báo của hàm CheckAvailablity sử dụng trong
ứng dụng để kiểm tra xem một username đã được đăng ký chưa. Để tập trung
vào chủ đề chính, tôi chỉ đơn giản lưu username vào một biến session,
như vậy là trong cùng một session sẽ không đăng ký được hai thành viên
có username giống nhau.
[AjaxPro.AjaxMethod]
public bool CheckAvailablity(string userName)
{
return (null == Session[userName]);
}
Hàm CheckAvailability hoàn toàn có thể được sử dụng lại
trong các hàm khác ở phía server mà không phải thay đổi gì cả, ví dụ hàm
này được dùng để kiểm tra username khi người sử dụng nhấn vào nút Sign
Up, phát sinh sự kiện postback gửi dữ liệu về xử lý trên server:
protected void btnSignUp_Click(object sender, EventArgs
e)
{
if (CheckAvailablity(txtUserName.Text))
{
// register new user...
}
// ...
}
Cũng tương tự cách khai báo như trên, chúng ta có hàm
GetStateList dùng để trả về danh sách các thành phố của quốc gia tương
ứng mỗi khi người sử dụng thay đổi tên quốc gia ở phía client. Hàm này
trả về một đối tượng kiểu DataTable, tuy nhiên với sự hỗ trợ của Ajax.NET
sẽ không có khó khăn gì để xử lý và hiển thị dữ liệu này trên form ở phía
client.
[AjaxPro.AjaxMethod]
public DataTable GetStateList(int countryIndex)
{
DataTable dt = new DataTable();
dt.Columns.Add("StateCode", typeof(string));
dt.Columns.Add("StateName", typeof(string));
// populate state list
return dt;
}
Cuối cùng, để các hàm trên có thể được gọi từ phía client
thì chúng ta cần phải đăng ký với Ajax.NET. Sau khi đăng ký với Ajax.NET,
thường thực hiện trong hàm xử lý sự kiện Page_Load của trang web, Ajax.NET
sẽ tự động sinh ra các mã lệnh JavaScript cần thiết của các hàm này ở
phía client. Khi các hàm ở client được gọi thực hiện, dữ liệu sẽ chuyển
tới các hàm tương ứng trên server để xử lý và trả về kết quả sau đó.
public partial class Tiendq_AjaxDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax(typeof(Tiendq_AjaxDemo));
if (false == IsPostBack)
{
// ...
}
}
// ...
}
Sử dụng hàm Ajax ở client
Sau khi xây dựng xong các hàm Ajax phía server, chúng
ta có thể sử dụng các hàm này ở client (mã lệnh viết bằng JavaScript)
với cú pháp như đã khai báo ở phía server (mã lệnh viết bằng C#), việc
gọi hàm có thể thực hiện đồng bộ (synchronous) hoặc bất đồng bộ (asynchronous).
Khi gọi hàm theo kiểu synchronous sẽ nhận được dữ liệu trả về tức thời
(hoặc phải chờ tuỳ theo tốc độ của kết nối tới server), khi gọi hàm theo
kiểu bất đồng bộ (asynchronous) thì dữ liệu sẽ được trả về qua một hàm
callback, đoạn mã lệnh thực hiện gọi hàm trong trường hợp này sẽ tiếp
tục thực hiện và không cần chờ dữ liệu trả về từ phía server. Hàm callback
sẽ được gọi khi có dữ liệu từ server trả về sau đó.
Gọi hàm theo kiểu đồng bộ:
function OnCheckAvailablity(userName)
{
var res = Tiendq_AjaxDemo.CheckAvailablity(userName);
alert(res.value);
}
Gọi hàm theo kiểu bất đồng bộ:
function OnCheckAvailablity(userName)
{
Tiendq_AjaxDemo.CheckAvailablity(userName,
OnCheckAvailablity_Callback);
}
function OnCheckAvailablity_Callback(res)
{
if (null != res.error)
{
alert("Error message goes here!");
return;
}
if (res.value)
{
// process data
}
else
{
// raise error
}
}
Trong cả hai trường hợp gọi hàm trên, dữ liệu trả về
là một đối tượng có cấu trúc như sau:
Tên trường |
Mô tả |
Value |
Dữ liệu trả về khi gọi thực hiện hàm phía server, kiểu dữ liệu
thực sự phụ thuộc vào khai báo hàm phía server (có thể là chuỗi
ký tự, số nguyên, mảng,...). |
Error |
Thông báo lỗi nếu có. |
Request |
Dữ liệu trả về của đối tượng XMLHttpRequest, trường hợp muốn lấy
thêm các thông tin trả về khác. Xem tài liệu về đối tượng XMLHttpRequest
để biết chi tiết. |
Context |
Tham chiếu tới đối tượng phía client. |
Sau đây sẽ là phần mã lệnh gọi thực hiện các hàm CheckAvailablity
và GetStateList ở phía client mà chúng ta đã khai báo ở phần trên. Hàm
CheckAvailablity sẽ được gọi thực hiện khi người sử dụng nhấn vào nút
Check để kiểm tra username muốn đăng ký đã tồn tại chưa, hàm GetStateList
được gọi mỗi khi người sử dụng chọn quốc gia khác nhau để cập nhật lại
danh sách thành phố của quốc gia đó:
function OnCheckAvailablity(userName)
{
if (userName.length > 0)
Tiendq_AjaxDemo.CheckAvailablity(userName,
OnCheckAvailablity_Callback);
else
alert("User name is required field.");
}
function OnCheckAvailablity_Callback(res)
{
if (null != res.error)
{
alert("Error message goes here!");
return;
}
if (res.value)
{
document.all("lblCheckStatus").innerHTML = "" + document.forms[0].txtUserName.value
+ " is available."
document.all("lblCheckStatus").className = "cssAvailable";
}
else
{
document.all("lblCheckStatus").innerHTML = "" + document.forms[0].txtUserName.value
+ " is not available."
document.all("lblCheckStatus").className = "cssNotAvailable";
}
}
function OnSelectCountry(countryIndex)
{
if (countryIndex > 0)
Tiendq_AjaxDemo.GetStateList(countryIndex, GetStateList_Callback);
else
{
ResetStateList();
document.forms[0].cboStates.disabled = true;
}
}
function GetStateList_Callback(res)
{
if (null != res.error)
{
alert("Error message goes here!");
return;
}
var dt = res.value;
if ((null != dt) && ("object"
== typeof(dt)))
{
document.forms[0].cboStates.disabled = false;
ResetStateList();
var oItem = null;
for (var i = 0; i < dt.Rows.length; ++ i)
{
oItem = document.createElement("OPTION");
oItem.text = dt.Rows[i].StateName;
oItem.value = dt.Rows[i].StateCode;
document.forms[0].cboStates.add(oItem);
}
}
}
Nhờ có Ajax.NET, ở đoạn mã lệnh JavaScript trên kiểu
dữ liệu phức tạp DataTable trả về từ hàm GetStateList có thể xử lý dễ
dàng theo cách quen thuộc như dùng ngôn ngữ của .NET (C#, VB.NET). Hoàn
toàn không cần thao tác với XML, hay DOM.
Một số chú ý
Ở ứng dụng này tôi mới chỉ minh hoạ trường hợp gọi hàm
được khai báo trong cùng một lớp của trang web, trường hợp khai báo hàm
trong một Web User Control hoặc trong một lớp khác thì cần phải đăng ký
với Ajax.NET như sau.
Khai báo đăng ký Ajax.NET cho Web User Control, trong
đó WebUserControl1 là tên của lớp user control có các hàm sử dụng Ajax:
AjaxPro.Utility.RegisterTypeForAjax(typeof(WebUserControl1),
this.Page);
Khai báo đăng ký với Ajax.NET cho lớp Class1 có các hàm
sử dụng Ajax:
AjaxPro.Utility.RegisterTypeForAjax(typeof(Class1));
Tương tự, nếu kiểu dữ liệu trả về là một đối tượng tự
xây dựng thì ta cũng phải đăng ký như Class1 ở trên.
Như đã đề cập ở phần trên của bài viết, có một số hạn
chế khi xây dựng các hàm Ajax để gọi từ phía client cần phải chú ý:
- Để truy cập các biến Request, Response, Session, Cache... cần phải
sử dụng thông qua biến HttpContext.Current.
- Nếu muốn hàm Ajax sử dụng các biến lưu trong session thì phải chỉ
rõ khi khai báo thuộc tính AjaxMethodAttribute. Ví dụ hàm CheckAvailablity
ở trên cần phải được khai báo đúng như sau:
AjaxPro.AjaxMethod(AjaxPro.HttpSessionStateRequirement.Read)]
public bool CheckAvailablity(string userName)
{
return (null == Session[userName]);
}
Trong đó HttpSessionStateRequirement là một enum định
nghĩa các kiểu truy nhập vào biến session mà hàm CheckAvailability sẽ
sử dụng.
Khi hàm Ajax được gọi sẽ không phát sinh ra sự kiện postback
ở phía server, do đó hàm xử lý sự kiện Page_Load cũng sẽ không được gọi,
và hàm Ajax không thể truy cập các control có trên trang web (server control).
Ứng dụng đơn giản trên chỉ là minh hoạ cho việc sử dụng
thư viện Ajax.NET với ASP.NET, còn nhiều tính năng nữa của thư viện này
chưa trình bày được trong phạm vi bài viết như caching, hỗ trợ chuyển
đổi kiểu dữ liệu, context... Bạn có thể tự tìm hiểu chi tiết sau khi đã
làm quen với thư viện này.
Lời kết
Bản thân Ajax là một khái niệm mới và thư viện Ajax.NET
vẫn đang còn được phát triển, nên còn một số hạn chế cũng như những thiếu
sót trong tài liệu. Tuy nhiên nếu yêu cầu của ứng dụng không quá phức
tạp thì nó hoàn toàn có thể đáp ứng tốt, giúp lập trình viên giảm đáng
kể công sức so với sử dụng trực tiếp đối tượng XMLHttpRequest. Thậm chí
chúng ta có thể sửa đổi mã nguồn của thư viện này để đáp ứng yêu cầu riêng
của mình nếu cần thiết. Nếu gặp vấn đề trong khi sử dụng thư viện này,
bạn đọc có thể tham gia vào nhóm thảo luận Ajax.NET tại http://groups.google.com/group/Ajaxpro
để tìm thông tin trợ giúp cũng như chia sẻ kinh nghiệm của mình.
Tài liệu tham khảo:
Đỗ Quyết Tiến
NetHoa Software
(theo PC World VN) |