(Post 19/08/2008) Từ khi xuất hiện vào đầu
năm 2005 trên một bài báo của Jesse James Garette, Ajax đã ngày càng trở
nên phổ biến và được ứng dụng rộng rãi, điển hình là các dịch vụ như Google
Maps, Google Suggest, Gmail, Yahoo! Mail beta... Đặc biệt là công cụ Google
Suggest gợi ý trước cho bạn một số từ khoá liên quan ngay khi bạn mới
chỉ gõ một phần của từ khoá và số lượng kết quả tìm kiếm có được nếu bạn
thực hiện tìm kiếm với từ gợi ý được chọn.
Cách gợi ý như của Google Suggest có thể áp dụng vào
rất nhiều ứng dụng khác nhau như từ điển, tìm kiếm nhanh trên một danh
sách dữ liệu... Và để áp dụng tính năng gợi ý đó vào trang web của bạn
cũng không quá khó. Bài viết này hướng dẫn cách xây dựng tính năng như
vậy cho một trang web, ngôn ngữ lập trình sử dụng là Java (đòi hỏi bạn
phải biết lập trình web với JSP, Servlet, biết sử dụng web server Tomcat,
lập trình Javascript, HTML, và một chút CSS).
Trang web chỉ có một trang duy nhất và có chức năng tìm
kiếm tên các nước trên thế giới. Khi bạn gõ một phần tên của nước cần
tìm vào ô tìm kiếm, Ajax sẽ hiển thị danh sách tên các nước có bắt đầu
bằng cụm từ mà bạn đang gõ, cùng một số thông tin liên quan như thủ đô,
dân số, diện tích và mã điện thoại quốc tế. Bạn có thể dùng bàn phím hoặc
chuột để chọn một nước trong danh sách này.
Để cho đơn giản, dữ liệu về các nước trên thế giới được
lưu trong một file text (trong thực tế có thể là một hoặc nhiều bảng trong
cơ sở dữ liệu). File này gồm có nhiều dòng, mỗi dòng là thông tin về một
nước, cấu trúc của một dòng như sau:
Tên nước-Thủ đô-Dân số-Diện tích-Mã điện thoại
Các thông tin trên một dòng được ngăn cách bằng dấu gạch
nối (-). Ví dụ:
Argentina-Kabul-21.92-647,497-54
Australia-Canbera-19.10-7,686,848-61
Austria-Vienna-8.10-83,853-43
Vietnam-Hanoi-84.51-329,556-84
Kiến trúc hoạt động của ứng dụng này xây dựng trên mô
hình ba lớp, được thể hiện như sau:
Trong đó ở tầng nghiệp vụ (Business tier) ta dùng web
server là Tomcat để triển khai ứng dụng web viết bằng Java, gồm có Servlet
để phân tích và xử lý yêu cầu từ trình duyệt (client); Business object
thực hiện việc tìm dữ liệu theo yêu cầu của client và chuyển kết quả tìm
được thành JavaBean. Tầng cơ sở dữ liệu (Database tier) có thể dùng các
hệ quản trị CSDL phổ biến như MySQL, MS SQL, ... Tuy nhiên để cho đơn
giản, trong ứng dụng này ta chỉ dùng một text file để lưu dữ liệu.
Sơ đồ các thao tác thực hiện ở phía server như sau:
Sơ đồ xử lý ở phía client như sau:
Như đã nói ở trên, để đơn giản ta không dùng hệ quản
trị CSDL nào mà chỉ dùng một file text để lưu thông tin về các nước trên
thế giới. Bạn tạo một text file có cấu trúc như mô tả ở trên và đặt tên
là countries.txt chẳng hạn.
Chúng ta tạo ra một JavaBean để chứa đựng thông tin về
các quốc gia, bao gồm các thuộc tính sau (tất cả các thuộc tính đều có
hàm setXxx() và getXxx(), trong đó Xxx là tên thuộc tính):
STT |
Tên thuộc tính |
Kiểu dữ liệu |
Mô tả |
1 |
area |
String |
Diện tích |
2 |
capital |
String |
Thủ đô |
3 |
code |
String |
Mã điện thoại |
4 |
name |
String |
Tên nước |
5 |
population |
String |
Dân số |
Lớp này ta đặt tên là CountryBean.java, mã nguồn rất
đơn giản:
package com.hainasoft.ajax.suggestion;
public class CountryBean {
private String name;
private String capital;
private String population;
private String area;
private String code;
public CountryBean(String name,
String capital, String pop, String area, String code) {
this.name = name;
this.capital = capital;
this.population = pop;
this.area = area;
this.code = code;
}
public String getName() {
return name;
}
public void setName(String
name) {
this.name = name;
}
public String getCapital()
{
return capital;
}
public String getArea() {
return area;
}
public void setCapital(String
capital) {
this.capital = capital;
}
public void setArea(String
area) {
this.area = area;
}
public String getPopulation()
{
return population;
}
public String getCode() {
return code;
}
public void setPopulation(String
population) {
this.population = population;
}
public void setCode(String
code) {
this.code = code;
}
}
Bạn để ý trong mã nguồn trên thì gói ứng dụng của chúng
ta là com.hainasoft.ajax.suggestion.
Tiếp theo chúng ta cần có một lớp để thực hiện thao tác
nghiệp vụ, ở ứng dụng này là đọc dữ liệu từ file text và ánh xạ (mapping)
các thuộc tính trong file text vào JavaBean. Chúng ta đọc qua từng dòng
trong file. Với mỗi dòng đọc được, ta tách các thuộc tính trong chuỗi
bằng ký tự ngăn cách là dấu gạch nối (-), kết quả được một mảng String
với phần tử thứ 0 là tên nước, phần tử thứ 1 là thủ đô, ... Sau đó chúng
ta tạo một object của lớp CountryBean để ánh xạ các thuộc tính này vào
thông qua constructor của lớp, và mỗi object chứa thông tin của một nước
này lại được thêm vào một List là kết quả trả về. Tất cả đặt trong một
phương thức duy nhất là getCountries() của lớp CountryManager.java, mã
nguồn của lớp này cũng rất đơn giản:
package com.hainasoft.ajax.suggestion;
import java.io.*;
import java.util.*;
public class CountryManager {
private static final String countryFile = "countries.txt";
public List getCountries() throws FileNotFoundException,
IOException {
List result = new ArrayList();
File f = new File(countryFile);
FileInputStream fis = new FileInputStream(f);
BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
String strLine = null;
while((strLine = reader.readLine()) != null) {
String[] lineData = strLine.split("-");
result.add(new CountryBean(lineData[0], lineData[1], lineData[2], lineData[3],
lineData[4]));
}
reader.close();
return result;
}
}
Tiếp theo chúng ta xây dựng một Servlet để nhận yêu cầu
từ client, phân tích yêu cầu này và trả kết quả tương ứng về cho client,
đặt tên cho servlet này là AjaxServlet.java. Mọi thứ chúng ta sẽ làm trong
phương thức doGet() của servlet, còn phương thức doPost() thì gọi lại
doGet(). Chúng ta xem qua mã nguồn trước:
package com.hainasoft.ajax.suggestion;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class AjaxServlet extends
HttpServlet {
private static final String
CONTENT_TYPE = "text/xml; charset=utf-8";
//Initialize global variables
public void init() throws ServletException {
}
//Process the HTTP Get request
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/* Get keyword from query string */
String keyword = request.getParameter("keyword");
if(keyword == null) keyword = "";
/* Set content type for response document */
response.setContentType(CONTENT_TYPE);
/* Set cache control on browser is no cache */
response.setHeader("Cache-Control", "no-cache");
/* Get writer from HttpServletResponse object */
PrintWriter out = response.getWriter();
/* XML document string response to client */
String xmlText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
/* Retrieve data from database or other data sources. In this sample application,
we read a list of country name and code from a text file which is stored
on server
*/
List countries = new CountryManager().getCountries();
if(countries != null && countries.size() > 0) {
xmlText += "<countrieslist>";
for (Iterator it = countries.iterator(); it.hasNext(); ) {
CountryBean country = (CountryBean) it.next();
if(country.getName().startsWith(keyword)) {
xmlText += "<country>";
xmlText += "<name>" + country.getName() + "</name>";
xmlText += "<capital>" + country.getCapital() + "</capital>";
xmlText += "<pop>" + country.getPopulation() + "</pop>";
xmlText += "<area>" + country.getArea() + "</area>";
xmlText += "<code>" + country.getCode() + "</code>";
xmlText += "</country>";
}
}
xmlText += "</countrieslist>";
} else {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
out.write(xmlText);
out.flush();
}
//Process the HTTP Post request
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
//Clean up resources
public void destroy() {
}
}
Khi người dùng gõ vào từ khóa trong ô tìm kiếm thì chuỗi
gõ vào sẽ được gửi tới servlet này, ví dụ:
AjaxServlet?keyword=vietnam
Servlet sẽ lấy ra giá trị của từ khóa gửi lên từ trình
duyệt bằng lệnh:
String keyword = request.getParameter("keyword");
Để xác định kiểu của nội dung trả về cho trình duyệt,
lệnh:
response.setContentType(CONTENT_TYPE);
sẽ đặt content type trả về cho trình duyệt là giá trị
được lưu trong hằng CONTENT_TYPE, hằng này được định nghĩa như sau:
private static final String
CONTENT_TYPE = "text/xml; charset=utf-8";
với câu lệnh trên thì kết quả trả về sẽ được trình duyệt
hiểu là tài liệu text hoặc XML và ở dạng mã unicode UTF-8.
Để trình duyệt không lưu cache của kết quả trả về khi
tương tác giữa trình duyệt và servlet, ta dùng câu lệnh:
response.setHeader("Cache-Control",
"no-cache");
Đây cũng là một câu lệnh rất phổ biến khi viết Servlet
trong các ứng dụng Ajax.
Nội dung trả về là một chuỗi thể hiện nội dung của file
XML, được lưu trong biến xmlText. Cấu trúc của file XML trả về cho trình
duyệt có dạng như sau:
<?xml version="1.0"
encoding="utf-8"?>
<countrieslist>
<country>
<name>...</name>
<capital>...</capital>
<pop>...</pop>
<area>...</area>
<code>...</code>
</country>
<country>
<name>...</name>
<capital>...</capital>
<pop>...</pop>
<area>...</area>
<code>...</code>
</country>
...
</countrieslist>
Tiếp theo ta tạo ra một đối tượng của lớp CountryManager,
gọi phương thức getCountries() của lớp này để lấy về danh sách tất cả
các nước có trong file countries.txt:
List countries = new CountryManager().getCountries();
Kiểm tra xem danh sách kết quả trả về không được null
và phải có chứa phần tử:
if(countries != null &&
countries.size() > 0)
nếu không, thì đặt trạng thái cho thông báo HTTP sẽ gửi
về cho trình duyệt là không có nội dung bằng hằng SC_NO_CONTENT của lớp
HttpServletResponse:
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
Nếu danh sách kết quả trả về chứa phần tử thì duyệt qua
lần lượt từng phần tử trong danh sách này, ép kiểu về đối tượng CountryBean
và kiểm tra xem thuộc tính name có bắt đầu bằng từ khóa người dùng vừa
gõ vào hay không, nếu có thì tạo ra một element XML có tên là country
với các element con là các thuộc tính của nước nó được lấy ra bằng các
phương thức get của JavaBean.
if(country.getName().startsWith(keyword))
{
xmlText += "<country>";
xmlText += "<name>" + country.getName() + "</name>";
xmlText += "<capital>" + country.getCapital() + "</capital>";
xmlText += "<pop>" + country.getPopulation() + "</pop>";
xmlText += "<area>" + country.getArea() + "</area>";
xmlText += "<code>" + country.getCode() + "</code>";
xmlText += "</country>";
}
Cuối cùng ghi kết quả về cho client thông qua phương
thức write của đối tượng PrintWriter:
out.write(xmlText);
out.flush();
Ở phía client chúng ta có một file HTML để hiển thị giao
diện và một thư viện Javascript để xử lý tương tác giữa client và server.
File HTML chúng ta đặt tên là index.html, còn file Javascript tên là ajaxsuggest.js.
File index.html tạo ra một text box để người dùng gõ
từ khóa vào:
<input type="text"
name="country" id="country" autocomplete="off"
onKeyUp="doSuggestion(event);"/>
Sự kiện onKeyUp của text box sẽ gọi hàm doSuggestion()
trong file ajaxsuggest.js (xem giải thích cụ thể trong mã nguồn). Một
thẻ <div> để hiển thị kết quả tìm kiếm:
<div id="divCountry"
style="position:relative; visibility:hidden; z-index:1"></div>
Chúng ta sẽ dùng Javascript để cập nhật mã HTML của thẻ
<div> này mỗi khi có kết quả trả về từ server.
Nội dung đầy đủ của file index.html:
<html>
<head>
<title>AJAX Suggestion Sample</title>
</head>
<script language="javascript" src="ajaxsuggest.js"></script>
<body>
<table border="0" width="100%">
<form method="POST">
<tr>
<td align="right">
Type part of your country name:
</td>
<td align="left">
<input type="text" name="country" id="country"
autocomplete="off" onKeyUp="doSuggestion(event);"/>
<input type="hidden" name="zipCode" id="zipCode"/>
</td>
</tr>
<tr>
<td></td>
<td align="left">
<div id="divCountry" style="position:relative; visibility:hidden;
z-index:1"></div>
</td>
</tr>
</form>
</table>
</body>
<script language="javascript">
setupSuggestion(
"ajaxservlet?keyword=",
"country",
"zipCode",
"divCountry",
"#000000",
"#FFFFFF",
"#99CCFF"
);
</script>
</html>
File cấu hình cho ứng dụng web là web.xml ta chỉ cần
cấu hình URL của servlet giống với tham số truyền vào trong lời gọi hàm
setupSuggestion() của file index.html, nội dung như sau:
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>AjaxSuggestion</display-name>
<servlet>
<servlet-name>Suggestion Ajax Servlet</servlet-name>
<servlet-class>com.hainasoft.ajax.suggestion.AjaxServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Suggestion Ajax Servlet</servlet-name>
<url-pattern>/ajaxservlet</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
</web-app>
Như vậy phần code cho ứng dụng đã xong, bạn biên dịch
các file .java và đóng gói thành file WAR cùng với các file index.html,
ajaxsuggest.js và web.xml. Triển khai ứng dụng trên web server là Tomcat
và chạy thử. File countries.txt bạn đặt tại thư mục cài web server hoặc
nếu bạn đặt ở thư mục khác thì phải chỉ ra đường dẫn trong hằng countryFile
của lớp CountryManager.
Gõ địa chỉ vào trình duyệt, ví dụ: http://localhost/AjaxSuggestion/index.html.
Nhập vào ô tìm kiếm chữ "a" thì ngay lập tức tên các nước bắt
đầu bằng chữ "a" hiện lên, dùng phím mũi tên lên, xuống hoặc
chuột để tô sáng tên nước cần chọn trong danh sách. Muốn chọn thì bấm
Enter hoặc nhấn chuột trái. Nếu bạn càng gõ thêm nhiều chữ cái nữa thì
danh sách kết quả sẽ càng thu hẹp dần.
Bạn có thể liên hệ với tạp chí Thế Giới Vi Tính hoặc
tác giả bài viết qua địa chỉ email để có mã nguồn đầy đủ (JBuiler project)
của ứng dụng này.
Hà Minh Nam
(theo PC World VN) |