PDF:

Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và
Ajax
Nâng cao các thành phần phức hợp bằng cách sử dụng các tính năng
JSF 2 mới
David Geary
Chủ tịch
Clarity Training, Inc.
09 09 2010
David Geary, thành viên nhóm chuyên gia Java™Server Faces (JSF) 2, kết thúc loạt bài ba
phần của ông về các tính năng mới của JSF 2. Tìm hiểu cách sử dụng mô hình sự kiện mới của
khung công tác và sự hỗ trợ kèm sẵn cho Ajax để làm cho tất cả các thành phần tái sử dụng của
bạn càng mạnh mẽ hơn.
Xem thêm bài trong loạt bài này
Một trong các điểm hấp dẫn lớn nhất của JSF là nó là một khung công tác dựa vào thành phần.
Điều đó có nghĩa là bạn hoặc những người khác có thể thực hiện các thành phần, các thành phần
có thể tái sử dụng. Cơ chế tái sử dụng mạnh mẽ đó, đối với hầu hết các phần, đã biểu hiện không
đáng kể trong JSF 1 vì đã rất khó triển khai thực hiện các thành phần.
Tuy nhiên, như bạn đã thấy trong Phần 2, JSF 2 làm cho dễ dàng triển khai thực hiện các thành
phần — không cần mã Java và không có cấu hình — với một tính năng mới được gọi là các thành
phần phức hợp. Tính năng đó có thể là phần quan trọng nhất của JSF 2, vì cuối cùng nó thực hiện
được tiềm năng của các thành phần JSF.
Trong bài thứ ba và là bài cuối cùng về JSF 2 này, tôi sẽ cho bạn thấy làm thế nào để cải thiện tính
năng của thành phần phức hợp bằng cách sử dụng Ajax mới và các khả năng xử lý sự kiện cũng
được đưa vào trong JSF 2, với các lời khuyên sau đây để khai thác tốt nhất JSF 2:
• Lời khuyên 1: Hãy thành phần hóa
• Lời khuyên 2: Hãy Ajax hóa
• Lời khuyên 3: Hãy cho xem tiến độ
Trong lời khuyên đầu tiên, tôi sẽ xem xét lại ngắn gọn hai thành phần mà tôi thảo luận chi tiết trong
Phần 2. Trong các lời khuyên sau đó, tôi sẽ cho bạn thấy làm thế nào để chuyển đổi các thành phần
đó bằng cách sử dụng Ajax và xử lý-sự kiện.
© Copyright IBM Corporation 2010
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Nhẫn hiệu đăng ký
Trang 1 của 19
developerWorks®
ibm.com/developerWorks/vn/
Lời khuyên 1: Hãy thành phần hóa
Ứng dụng các địa điểm, mà tôi đã giới thiệu trong Phần 1, có chứa một số thành phần phức hợp.
Một là thành phần map (bản đồ), hiển thị một bản đồ của một địa chỉ, bổ sung thêm một trình đơn
thả xuống gồm các mức phóng to, như trong Hình 1:
Hình 1. Thành phần map của ứng dụng các địa điểm
Liệt kê mã rút gọn của thành phần map được hiển thị trong Liệt kê 1:
Liệt kê 1. Thành phần map
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml"
...
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:places="http://java.sun.com/jsf/composite/components/places">
<!-- INTERFACE -->
<composite:interface>
<composite:attribute name="title"/>
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation">
<div class="map">
...
<h:panelGrid...>
<h:panelGrid...>
<h:selectOneMenu onchange="submit()"
value="#{cc.parent.attrs.location.zoomIndex}"
valueChangeListener="#{cc.parent.attrs.location.zoomChanged}"
style="font-size:13px;font-family:Palatino">
<f:selectItems value="#{places.zoomLevelItems}"/>
</h:selectOneMenu>
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 2 của 19
ibm.com/developerWorks/vn/
developerWorks®
</h:panelGrid>
</h:panelGrid>
<h:graphicImage url="#{cc.parent.attrs.location.mapUrl}"
style="border: thin solid gray"/>
...
</div>
...
</composite:implementation>
</html>
Một trong những điều tuyệt vời của các thành phần là bạn có thể thay thế chúng bằng các lựa chọn
thay thế khác, mạnh hơn mà không làm ảnh hưởng bất kỳ các chức năng xung quanh nào. Ví dụ,
trong Hình 2, tôi đã thay thế thành phần image (hình ảnh) trong Liệt kê 1 bằng thành phần Google
Maps, với sự cho phép của GMaps4JSF (xem Tài nguyên):
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 3 của 19
developerWorks®
ibm.com/developerWorks/vn/
Hình 2. Hình ảnh bản đồ của GMaps4JSF
Mã được cập nhật (và được cắt ngắn bớt) cho thành phần map được hiển thị trong Liệt kê 2:
Liệt kê 2. Thay thế hình ảnh bản đồ bằng một thành phần GMaps4JSF
<h:selectOneMenu onchange="submit()"value="#{cc.parent.attrs.location.zoomIndex}"
valueChangeListener="#{cc.parent.attrs.location.zoomChanged}"
style="font-size:13px;font-family:Palatino">
<f:selectItems value="#{places.zoomLevelItems}"/>
</h:selectOneMenu>
...
<m:map id="map" width="420px" height="400px"
address="#{cc.parent.attrs.location.streetAddress}, ..."
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 4 của 19
ibm.com/developerWorks/vn/
developerWorks®
zoom="#{cc.parent.attrs.location.zoomIndex}"
renderOnWindowLoad="false">
<m:mapControl id="smallMapCtrl"
name="GLargeMapControl"
position="G_ANCHOR_TOP_RIGHT"/>
<m:mapControl id="smallMapTypeCtrl" name="GMapTypeControl"/>
<m:marker id="placeMapMarker"/>
</m:map>
Để sử dụng một thành phần GMaps4JSF, tôi đã thay thế thẻ <h:graphicImage> bằng một thẻ
<m:map> từ bộ thành phần GMaps4JSF. Cũng thật đơn giản để móc nối thành phần GMaps4JSF
vào trình đơn thả xuống các mức phóng to, chỉ cần chỉ rõ thuộc tính bean hậu thuẫn chính xác cho
thuộc tính zoom (phóng to) của thẻ <m:map>.
Khi nói về các mức phóng to, chú ý rằng khi một người dùng thay đổi mức phóng to, tôi bắt buộc
gửi đi một biểu mẫu có thuộc tính onchange của thẻ <h:selectOneMenu>, như được hiển thị trong
dòng đầu tiên được in đậm một phần trong Liệt kê 1. Việc gửi biểu mẫu đó kích hoạt vòng đời JSF,
mà cuối cùng đẩy giá trị mới cho mức phóng to vào thuộc tính zoomIndex của một bean location
(vị trí) được lưu trong thành phần phức hợp cha mẹ. Thuộc tính bean đó được liên kết với thành
phần đầu vào, trong dòng đầu tiên của Liệt kê 2.
Vì tôi đã không xác định bất kỳ sự chuyển hướng nào để gửi đi biểu mẫu kết hợp với việc thay đổi
mức phóng to, nên JSF làm mới chính trang này sau khi xử lý các yêu cầu, vẽ lại hình ảnh bản đồ
để phản ánh mức phóng to mới. Tuy nhiên, việc làm mới trang đó cũng vẽ lại toàn bộ trang mặc dù
sự thay đổi duy nhất chỉ ở trong hình ảnh bản đồ. Trong Lời khuyên 2: Hãy Ajax hóa, tôi sẽ chỉ cho
bạn cách sử dụng Ajax để chỉ vẽ lại hình ảnh để đáp ứng một sự thay đổi mức phóng to.
Thành phần login
Một thành phần khác được sử dụng trong ứng dụng các địa điểm là thành phần login (đăng nhập).
Hình 3 cho thấy thành phần login đang hoạt động:
Hình 3. Thành phần login
Liệt kê 3 cho thấy tài liệu đánh dấu siêu văn bản tạo ra thành phần login hiển thị trong Hình 3:
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 5 của 19
developerWorks®
ibm.com/developerWorks/vn/
Liệt kê 3. login (đăng nhập) tối thiểu: Chỉ các thuộc tính cần thiết
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:util="http://java.sun.com/jsf/composite/components/util">
<util:login loginAction="#{user.login}"
managedBean="#{user}"/>
</ui:composition>
Thành phần login chỉ có hai thuộc tính cần thiết phải có:
• loginAction: Một phương thức hành động đăng nhập.
• managedBean: Một bean được quản lý có các thuộc tính tên và mật khẩu.
Bean được quản lý đã xác định trong Liệt kê 3 được hiển thị trong Liệt kê 4:
Liệt kê 4. User.groovy
package com.clarity
import javax.faces.context.FacesContext
import javax.faces.bean.ManagedBean
import javax.faces.bean.SessionScoped
@ManagedBean()
@SessionScoped
public class User {
private final String VALID_NAME
= "Hiro"
private final String VALID_PASSWORD = "jsf"
private String name, password;
public String getName() { name }
public void setName(String newValue) { name = newValue }
public String getPassword() { return password }
public void setPassword(String newValue) { password = newValue }
public String login() {
"/views/places"
}
public String logout() {
name = password = nameError = null
"/views/login"
}
}
Bean được quản lý trong Liệt kê 4 là một bean Groovy. Việc sử dụng Groovy thay cho ngôn ngữ
Java không mang lại gì nhiều cho tôi trong trường hợp này, ngoài việc giải phóng tôi khỏi công việc
cực nhọc và buồn tẻ của các dấu chấm phẩy và các câu lệnh return. Tuy nhiên, trong phần Xác
nhận hợp lệ (Validation) của Lời khuyên 2, tôi sẽ cho bạn thấy một lý do thuyết phục hơn để sử dụng
Groovy đối với bean được quản lý User.
Hầu hết trường hợp, bạn sẽ muốn cấu hình đầy đủ các thành phần đăng nhập với các lời nhắc và
văn bản kèm theo nút, như trong Hình 4:
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 6 của 19
ibm.com/developerWorks/vn/
developerWorks®
Hình 4. Một thành phần login được cấu hình đầy đủ
Liệt kê 5 cho thấy tài liệu đánh dấu siêu văn bản tạo ra thành phần login trong Hình 4:
Liệt kê 5. Cấu hình thành phần login
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:util="http://java.sun.com/jsf/composite/components/util">
<util:login loginPrompt="#{msgs.loginPrompt}"
namePrompt="#{msgs.namePrompt}"
passwordPrompt="#{msgs.passwordPrompt}"
loginButtonText="#{msgs.loginButtonText}"
loginAction="#{user.login}"
managedBean="#{user}"/>
</ui:composition>
Trong Liệt kê 5, tôi nhận được các chuỗi ký tự với lời nhắc và văn bản kèm theo nút đăng nhập từ
một gói tài nguyên.
Liệt kê 6 định nghĩa thành phần login:
Liệt kê 6. Định nghĩa thành phần login
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- Usage:
<util:login loginPrompt="#{msgs.loginPrompt}"
namePrompt="#{msgs.namePrompt}"
passwordPrompt="#{msgs.passwordPrompt}"
loginButtonText="#{msgs.loginButtonText}"
loginAction="#{user.login}"
managedBean="#{user}">
<f:actionListener for="loginButton"
type="com.clarity.LoginActionListener"/>
</util:login>
managedBean must have two properties: name and password.
The loginAction attribute must be an action method that takes no
arguments and returns a string. That string is used to navigate
to the page the user sees after logging in.
This component's loginButton is accessible so that you can
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 7 của 19
developerWorks®
ibm.com/developerWorks/vn/
add action listeners to it, as depicted above. The class specified
in f:actionListener's type attribute must implement the
javax.faces.event.ActionListener interface.
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<!-- INTERFACE -->
<composite:interface>
<!-- PROMPTS -->
<composite:attribute name="loginPrompt"/>
<composite:attribute name="namePrompt"/>
<composite:attribute name="passwordPrompt"/>
<!-- LOGIN BUTTON -->
<composite:attribute name="loginButtonText"/>
<!-- loginAction is called when the form is submitted -->
<composite:attribute name="loginAction"
method-signature="java.lang.String login()"
required="true"/>
<!-- You can add listeners to this actionSource: -->
<composite:actionSource name="loginButton" targets="form:loginButton"/>
<!-- BACKING BEAN -->
<composite:attribute name="managedBean" required="true"/>
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<div class="prompt">
#{cc.attrs.loginPrompt}
</div>
<!-- FORM -->
<h:form id="form">
<h:panelGrid columns="2">
<!-- NAME AND PASSWORD FIELDS -->
#{cc.attrs.namePrompt}
<h:inputText id="name"
value="#{cc.attrs.managedBean.name}"/>
#{cc.attrs.passwordPrompt}
<h:inputSecret id="password" size="8"
value="#{cc.attrs.managedBean.password}"/>
</h:panelGrid>
<p>
<!-- LOGIN BUTTON -->
<h:commandButton id="loginButton"
value="#{cc.attrs.loginButtonText}"
action="#{cc.attrs.loginAction}"/>
</p>
</h:form>
</composite:implementation>
</html>
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 8 của 19
ibm.com/developerWorks/vn/
developerWorks®
Cũng giống như thành phần map (bản đồ), thành phần login có thể sử dụng nâng cấp bằng Ajax.
Trong mục Xác nhận hợp lệ của lời khuyên tiếp theo, tôi sẽ cho bạn thấy làm thế nào để thêm việc
xác nhận hợp lệ bằng Ajax cho thành phần đăng nhập login.
Lời khuyên 2: Hãy Ajax hóa
Ajax điển hình thường đòi hỏi hai bước mà thông thường các yêu cầu HTTP không Ajax đã không
làm như thế: xử lý từng phần các biểu mẫu trên máy chủ và biểu hiện từng phần kết quả của Mô
hình đối tượng tài liệu (Document Object Model-DOM) trên máy khách.
Xử lý và biểu hiện từng phần
JSF 2 hỗ trợ xử lý và biểu hiện từng phần bằng cách phân tách vòng đời của JSF thành hai phần
lô-gic riêng biệt: thi hành và biểu hiện. Hình 5 nêu bật phần thi hành:
Hình 5. Phần thi hành của vòng đời JSF
Hình 6 nêu bật phần biểu hiện của vòng đời của JSF:
Hình 6. Phần biểu hiện của vòng đời JSF
Ý tưởng đằng sau các phần thi hành và biểu hiện trong vòng đời là đơn giản: bạn có thể quy định
các thành phần mà JSF thi hành (xử lý) chúng trên máy chủ và các thành phần mà JSF biểu hiện
khi một cuộc gọi Ajax trả về. Bạn làm điều đó với thẻ <f:ajax>, là thẻ mới cho JSF 2, như thể hiện
trong Liệt kê 7:
Liệt kê 7. Trình đơn phóng to của Ajax
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<h:selectOneMenu id="menu"
value="#{cc.parent.attrs.location.zoomIndex}"
style="font-size:13px;font-family:Palatino">
<f:ajax event="change" execute="@this" render="map"/>
<f:selectItems value="#{places.zoomLevelItems}"/>
</h:selectOneMenu>
<m:map id="map"...>
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 9 của 19
developerWorks®
ibm.com/developerWorks/vn/
Liệt kê 7 là sửa đổi của trình đơn được chỉ ra trong dòng đầu tiên của Liệt kê 2: Tôi đã gỡ bỏ thuộc
tính onchange khỏi Liệt kê 2 và đã thêm một thẻ <f:ajax>. Thẻ <f:ajax> đó xác định:
• Sự kiện kích hoạt cuộc gọi Ajax.
• Một thành phần để thi hành trên máy chủ.
• Một thành phần để biểu hiện trên máy khách.
Khi người sử dụng chọn một mục từ trình đơn phóng to, JSF bắt đầu một cuộc gọi Ajax đến máy
chủ. Sau đó, JSF chuyển trình đơn đến phần thi hành của vòng đời (@this có nghĩa là thành phần
bao quanh của <f:ajax>) và cập nhật zoomIndex của trình đơn trong giai đoạn Cập nhật các giá trị
mô hình của vòng đời. Khi cuộc gọi Ajax trả về, JSF biểu hiện thành phần map (bản đồ), sử dụng
chỉ số phóng to (vừa mới được thiết lập) để vẽ lại bản đồ và bây giờ bạn có một trình đơn phóng to
được xử lý bằng Ajax, chỉ thêm một dòng XHTML.
Nhưng còn có thể làm mọi việc đơn giản hơn nữa, bởi vì JSF cung cấp các giá trị mặc định cho các
thuộc tính event (sự kiện) và execute (thi hành).
Mỗi thành phần JSF có một sự kiện mặc định để kích hoạt các cuộc gọi Ajax nếu bạn nhúng
một thẻ <f:ajax> bên trong thẻ thành phần. Đối với các trình đơn, sự kiện đó là sự kiện change
(thay đổi). Điều đó có nghĩa là tôi có thể bỏ thuộc tính event của <f:ajax> trong Liệt kê 7. Giá trị
mặc định cho thuộc tính execute của <f:ajax> là @this, nghĩa là thành phần bao quanh của thẻ
<f:ajax>. Trong ví dụ này, thành phần đó là trình đơn, vì vậy tôi cũng có thể bỏ thuộc tính execute.
Bằng cách sử dụng các giá trị thuộc tính mặc định cho <f:ajax>, tôi có thể rút gọn Liệt kê 7 thành
Liệt kê 8:
Liệt kê 8. Phiên bản đơn giản hơn của một trình đơn Ajax phóng to
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<h:selectOneMenu id="menu"
value="#{cc.parent.attrs.location.zoomIndex}"
style="font-size:13px;font-family:Palatino">
<f:ajax render="map"/>
<f:selectItems value="#{places.zoomLevelItems}"/>
</h:selectOneMenu>
<m:map id="map"...>
Điều này cho thấy việc thêm Ajax cho các thành phần của bạn với JSF 2 dễ dàng như thế nào. Tất
nhiên, ví dụ trên là khá đơn giản: Tôi đơn giản chỉ vẽ lại bản đồ thay vì toàn bộ cả trang khi người sử
dụng chọn một mức phóng to. Một số phép toán, như là việc xác nhận hợp lệ một trường riêng biệt
trong một biểu mẫu, sẽ phức tạp hơn, do đó, tiếp theo tôi sẽ giải quyết các trường hợp sử dụng đó.
Xác nhận hợp lệ
Xác nhận hợp lệ các trường và cung cấp thông tin phản hồi ngay lập tức khi người sử dụng nhập
xong dữ liệu và ra khỏi một trường là một ý tưởng tốt. Ví dụ, trong Hình 7, tôi đang sử dụng Ajax
để xác nhận hợp lệ trường tên:
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 10 của 19
ibm.com/developerWorks/vn/
developerWorks®
Hình 7. Xác nhận hợp lệ của Ajax
Mã đánh dấu siêu văn bản tạo ra trường tên được hiển thị trong Liệt kê 9:
Liệt kê 9. Trường name
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<h:panelGrid columns="2">
#{cc.attrs.namePrompt}
<h:panelGroup>
<h:inputText id="name" value="#{cc.attrs.managedBean.name}"
valueChangeListener="#{cc.attrs.managedBean.validateName}">
<f:ajax event="blur" render="nameError"/>
</h:inputText>
<h:outputText id="nameError"
value="#{cc.attrs.managedBean.nameError}"
style="color: red;font-style: italic;"/>
</h:panelGroup>
...
</h:panelGrid>
Một lần nữa, tôi sử dụng <f:ajax>, chỉ có điều là lần này sự kiện mặc định cho đầu vào —change—
sẽ không áp dụng được, vì vậy tôi xác định blur (làm mờ đi) là sự kiện kích hoạt cuộc gọi Ajax. Khi
người sử dụng ra khỏi trường tên, JSF bắt đầu một cuộc gọi Ajax đến máy chủ và chạy thành phần
đầu vào name thuộc giai đoạn thi hành của vòng đời. Điều này có nghĩa rằng JSF sẽ gọi trình nghe
thay đổi giá trị đầu vào của name như đã chỉ rõ trong Liệt kê 9 trong giai đoạn xác nhận hợp lệ của
vòng đời. Liệt kê 10 hiển thị trình nghe thay đổi giá trị ấy:
Liệt kê 10. Phương thức validateName()
package com.clarity
import
import
import
import
import
javax.faces.context.FacesContext
javax.faces.bean.ManagedBean
javax.faces.bean.SessionScoped
javax.faces.event.ValueChangeEvent
javax.faces.component.UIInput
@ManagedBean()
@SessionScoped
public class User {
private String name, password, nameError;
...
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 11 của 19
developerWorks®
ibm.com/developerWorks/vn/
public void validateName(ValueChangeEvent e) {
UIInput nameInput = e.getComponent()
String name = nameInput.getValue()
if (name.contains("_"))
nameError = "Name cannot contain underscores"
else if (name.equals("")) nameError = "Name cannot be blank"
else
nameError = ""
}
...
}
Trình nghe thay đổi-giá trị — Phương thức validateName() của bean được quản lý user (người sử
dụng) — xác nhận hợp lệ trường tên và cập nhật thuộc tính nameError của bean được quản lý user.
Sau khi cuộc gọi Ajax trả về, nhờ có thuộc tính render của thẻ <f:ajax> trong Liệt kê 9, JSF biểu
hiện kết quả đầu ra nameError. Đầu ra đó hiển thị thuộc tính nameError của bean được quản lý user.
Xác nhận hợp lệ nhiều trường
Trong tiểu mục trên, tôi đã cho bạn thấy làm thế nào để thực hiện xác nhận hợp lệ bằng Ajax cho
một trường duy nhất. Tuy nhiên, đôi khi bạn cần phải xác nhận hợp lệ nhiều trường cùng một lúc.
Ví dụ, Hình 8 cho thấy ứng dụng các địa điểm xác nhận hợp lệ các trường tên và mật khẩu cùng
nhau:
Hình 8. Xác nhận hợp lệ nhiều trường
Tôi xác nhận hợp lệ trường tên và mật khẩu cùng nhau khi người sử dụng gửi đi biểu mẫu, vì vậy tôi
không cần Ajax cho ví dụ này. Thay vào đó, tôi sẽ sử dụng hệ thống sự kiện mới của JSF 2, như thể
hiện trong Liệt kê 11:
Liệt kê 11. Sử dụng <f:event>
<h:form id="form" prependId="false">
<f:event type="postValidate"
listener="#{cc.attrs.managedBean.validate}"/>
...
</h:form>
<div class="error" style="padding-top:10px;">
<h:messages layout="table"/>
</div>
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 12 của 19
ibm.com/developerWorks/vn/
developerWorks®
Trong Liệt kê 11, tôi sử dụng <f:event>, — giống như <f:ajax> — là điểm mới của JSF 2. Thẻ
<f:event> cũng tương tự như <f:ajax> ở khía cạnh khác, đó là: sử dụng rất đơn giản.
Bạn đặt một thẻ <f:event> bên trong một thẻ thành phần và khi sự kiện đã xác định (bằng thuộc
tính type) xảy ra đối với thành phần đó, JSF gọi một phương thức, chỉ rõ bởi thuộc tính listener
(trình nghe). Vì vậy, theo tiếng Anh, thẻ <f:event> trong Liệt kê 11 có nghĩa là: Sau khi xác nhận
hợp lệ biểu mẫu, gọi phương thức validate() của bean được quản lý mà người sử dụng đã chuyển
cho thành phần phức hợp này. Đó là phương thức được chỉ ra trong Liệt kê 12:
Liệt kê 12. Phương thức validate()
package com.clarity
import
import
import
import
import
javax.faces.context.FacesContext
javax.faces.bean.ManagedBean
javax.faces.bean.SessionScoped
javax.faces.event.ValueChangeEvent
javax.faces.component.UIInput
@ManagedBean()
@SessionScoped
public class User {
private final String VALID_NAME
= "Hiro";
private final String VALID_PASSWORD = "jsf";
...
public void validate(ComponentSystemEvent e) {
UIForm form = e.getComponent()
UIInput nameInput = form.findComponent("name")
UIInput pwdInput = form.findComponent("password")
if ( ! (nameInput.getValue().equals(VALID_NAME) &&
pwdInput.getValue().equals(VALID_PASSWORD))) {
FacesContext fc = FacesContext.getCurrentInstance()
fc.addMessage(form.getClientId(),
new FacesMessage("Name and password are invalid. Please try again."))
fc.renderResponse()
}
}
...
}
JSF chuyển cho phương thức validate() trong Liệt kê 12 một sự kiện hệ thống thành phần, từ đó
phương thức này nhận được một tham chiếu đến thành phần mà sự kiện này áp vào nó — đó là
biểu mẫu đăng nhập. Từ biểu mẫu đó, tôi sử dụng phương thức findComponent() để nhận được các
thành phần tên và mật khẩu. Nếu các giá trị của các thành phần đó tương ứng không phải là Hiro
và JSF, tôi lưu thông báo trên bối cảnh faces (các mặt ngoài) và ra lệnh cho JSF bắt đầu ngay giai
đoạn đáp ứng biểu hiện (Render Response) của vòng đời. Theo cách này, tôi tránh được giai đoạn
Cập nhật các giá trị mô hình (Update Model Values), trong đó sẽ đẩy tên và mật khẩu không đúng
đến mô hình (xem Hình 5).
Bạn có thể đã nhận thấy rằng các phương thức xác nhận hợp lệ trong Liệt kê 10 và Liệt kê 12 được
viết bằng Groovy. Không giống như Liệt kê 4, ở đó lợi thế duy nhất để sử dụng Groovy là không có
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 13 của 19
developerWorks®
ibm.com/developerWorks/vn/
các dấu chấm phẩy và các câu lệnh return, mã Groovy trong Liệt kê 10 và Liệt kê 12 giải phóng
tôi khỏi phải ép kiểu (casting). Ví dụ, trong Liệt kê 10, ComponentSystemEvent.getComponent() và
UIComponent.findComponent() cả hai đều trả về kiểu UIComponent. Với ngôn ngữ Java, tôi sẽ phải
ép kiểu các giá trị trả về của các phương thức đó. Groovy thực hiện ép kiểu cho tôi.
Lời khuyên 3: Hãy cho xem tiến độ
Trong phần Hãy Ajax hóa, tôi đã cho bạn thấy làm thế nào để xử lý bằng Ajax trình đơn phóng to
cho thành phần map, sao cho ứng dụng các địa điểm chỉ vẽ lại phần bản đồ của trang khi người dùng
thay đổi mức phóng to. Một trường hợp phổ biến sử dụng Ajax là cung cấp một phản hồi đến người
dùng, khi một sự kiện Ajax đang tiến triển, như Hình 9:
Hình 9. Một thanh tiến độ
Trong Hình 9, tôi đã thay thế trình đơn phóng to bằng một hoạt hình GIF, hiển thị trong khi cuộc
gọi Ajax đang tiến triển. Khi cuộc gọi Ajax hoàn thành, tôi thay thế bộ chỉ thị tiến độ bằng trình đơn
phóng to. Liệt kê 13 cho thấy nó được thực hiện như thế nào:
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 14 của 19
ibm.com/developerWorks/vn/
developerWorks®
Liệt kê 13. Theo dõi một yêu cầu của Ajax
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<h:selectOneMenu id="menu"
value="#{cc.parent.attrs.location.zoomIndex}"
style="font-size:13px;font-family:Palatino">
<f:ajax render="map" onevent="zoomChanging"/>
<f:selectItems value="#{places.zoomLevelItems}"/>
...
</h:selectOneMenu>
...
<h:graphicImage id="progressbar" style="display: none"
library="images" name="orange-barber-pole.gif"/>
Trong Liệt kê 13, tôi thêm một hình ảnh thanh tiến độ, ban đầu không được hiển thị và xác định
thuộc tính onevent cho <f:ajax>. Thuộc tính này tham chiếu một hàm JavaScript, hiển thị trong
Liệt kê 14, mà JSF sẽ gọi hàm đó khi cuộc gọi Ajax bắt đầu trong Liệt kê 13 đang tiến triển:
Liệt kê 14. JavaScript để đáp ứng một yêu cầu Ajax
function zoomChanging(data) {
var menuId = data.source.id;
var progressbarId = menuId.substring(0, menuId.length - "menu".length)
+ "progressbar";
if (data.name == "begin") {
Element.hide(menuId);
Element.show(progressbarId);
}
else if (data.name == "success") {
Element.show(menuId);
Element.hide(progressbarId);
}
}
Trong Liệt kê 14, JSF chuyển cho hàm này một đối tượng có chứa một số thông tin, như là mã
nhận dạng máy khách của thành phần đã bắt đầu thực hiện sự kiện (trong trường hợp này, đó là
trình đơn mức phóng to) và trạng thái hiện tại của yêu cầu Ajax, được biểu diễn bởi một thuộc tính
được đặt tên không thích đáng lắm là name.
Hàm zoomChanging() hiển thị trong Liệt kê 14 tính toán mã nhận dạng máy khách của hình ảnh
thanh tiến trình và sau đó sử dụng đối tượng Element (phần tử) nguyên mẫu để ẩn giấu và hiển thị
các phần tử HTML thích hợp trong thời gian cuộc gọi Ajax.
Kết luận
Trong những năm qua, JSF 1 đã có tiếng như là một khung công tác rất khó sử dụng. Trong nhiều
khía cạnh, danh tiếng đó là đúng. JSF 1 đã được phát triển trong một tháp ngà mà không nhận
thức ra được rằng việc sử dụng trong thế giới thực mới có đủ khả năng. Kết quả là, JSF đã tạo ra
cho nó nhiều khó khăn để triển khai các ứng dụng và các thành phần hơn nó vốn có.
JSF 2, mặt khác, đã được sinh ra từ thử thách khắc nghiệt của thế giới thực, bởi công chúng, những
người đã triển khai thực hiện các dự án mã nguồn mở bên trên JSF 1. Các nhận thức sau này
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 15 của 19
developerWorks®
ibm.com/developerWorks/vn/
trong thế giới thực đã dẫn đến một khung công tác hiểu biết nhiều hơn, làm cho dễ dàng triển khai
thực hiện các ứng dụng Ajax hóa mạnh mẽ.
Trong suốt loạt bài này, tôi đã cho bạn thấy một số đặc tính JSF 2 nổi bật nhất, chẳng hạn như các
chú giải và quy ước để thay thế việc cấu hình, việc dẫn hướng được đơn giản hóa, hỗ trợ cho sử
dụng tài nguyên, các thành phần phức hợp, Ajax gắn sẵn và mô hình sự kiện mở rộng. Nhưng JSF
2 có nhiều đặc tính hơn nữa mà tôi đã không trình bày được trong loạt bài này, chẳng hạn như các
phạm vi của View và Page (Khung nhìn và Trang), hỗ trợ cho các trang có khả năng đánh dấu và
Giai đoạn dự án (Project Stage). Tất cả những đặc tính đó và nhiều hơn nữa, làm cho JSF 2 được
cải tiến rất nhiều so với phiên bản ban đầu của nó.
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 16 của 19
ibm.com/developerWorks/vn/
developerWorks®
Các tải về
Mô tả
Tên
Kích thước
Source code for the article examples
j-jsf2-fu-3.zip
7.7MB
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 17 của 19
developerWorks®
ibm.com/developerWorks/vn/
Tài nguyên
Học tập
• Trang chủ JSF: Tìm thêm tài nguyên về phát triển với JSF.
• GMaps4JSF: Thư viện này giúp những người sử dụng JSF xây dựng ứng dụng trộn, sử dụng
các bản đồ của Google (Google Maps).
• Blog của Roger Kitain: Roger Kitain và Ed Burns cùng đứng đầu nhóm đặc tả cho JSF 2.0.
• Blog của Jim Driscoll: Bạn sẽ tìm thấy nhiều mục có liên quan đến JSF 2.
• Blog của Ryan Lubke: Ryan Lubke làm việc về triển khai thực hiện tham chiếu JSF 2.
• Vùng công nghệ Java của developerWorks: Tìm hàng trăm bài viết về mọi khía cạnh về lập
trình Java.
Lấy sản phẩm và công nghệ
• JSF: Tải về JSF 2.0
• GMaps4JSF: Tải về GMaps4JSF
Thảo luận
• "Truy cập công khai đến các cuộc thảo luận JSF 2.0 JSR-314-EG hiện đã có sẵn" (Ed Burns's
Blog, java.net, 03.2009): Tìm hiểu cách đăng ký vào danh sách gửi thư của nhóm chuyên gia
JSF2.
• Dành tâm trí cho cộng đồng My developerWorks.
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 18 của 19
ibm.com/developerWorks/vn/
developerWorks®
Đôi nét về tác giả
David Geary
David Geary, tác giả của quyển sách Core HTML5 Canvas, cũng là đồng sáng lập
của Nhóm người dùng HTML5 Denver và là tác giả của 8 cuốn sách Java, bao gồm
cả những cuốn sách bán chạy nhất về Swing và JavaServer Faces. David là một diễn
giả thường xuyên tại các hội nghị, bao gồm JavaOne, Devoxx, Loop Strange, NDC và
OSCON và ông đã ba lần đạt danh hiệu JavaOne Rock Star (diễn giả hàng đầu tại hội
nghị JavaOne). Ông đã viết loạt bài JSF 2 fu và GWT fu cho developerWorks. Bạn có
thể theo dõi David trên Twitter tại @davidgeary.
© Copyright IBM Corporation 2010
(www.ibm.com/legal/copytrade.shtml)
Nhẫn hiệu đăng ký
(www.ibm.com/developerworks/vn/ibm/trademarks/)
Sức mạnh của JSF 2, Phần 3: Xử lý sự kiện, JavaScript và Ajax
Trang 19 của 19