markdown:
## Trước khi bạn bắt đầu
### Về loạt bài này
Giới thiệu mở đầu về công nghệ Java™ Server Faces (JSF), một khung công tác thành phần giao diện người dùng phía máy chủ cho các ứng dụng web dựa trên Java. Loạt bài này dành cho các nhà phát triển, những người mới bắt đầu tìm hiểu JSF và muốn tiến nhanh — không chỉ với JSF, mà với cả việc sử dụng các thành phần JSF để giảm công sức. Loạt bài này trình bày chỉ những điều cốt yếu, với rất nhiều ví dụ.
JSF là một môi trường phát triển GUI khá truyền thống, giống như AWT, SWT, và Swing. Một trong những lợi ích chính của nó là nó làm cho việc phát triển Web dễ dàng hơn bằng cách giao những công việc khó khăn cho các nhà phát triển khung công tác, chứ không phải cho các nhà phát triển ứng dụng. Cứ cho là bản thân JSF phức tạp hơn nhiều so với các khung công tác Web khác, nhưng sự phức tạp này được che giấu không để cho các nhà phát triển ứng dụng biết. Phát triển các ứng dụng Web trong JSF dễ dàng hơn nhiều so với hầu hết các khung công tác khác: nó đòi hỏi viết mã ít hơn, ít phức tạp hơn, và ít việc cấu hình hơn.
Nếu bạn đang thực hiện phát triển Java phía máy chủ, JSF là khung công tác dễ nhất để tìm hiểu. Nó được định hướng để tạo các ứng dụng Web (không chỉ là các trang web). Nó cho phép bạn tập trung vào việc mã hóa Java của bạn mà không cần đối phó với các đối tượng yêu cầu, các đối tượng phiên, các thông số yêu cầu, hoặc đối phó với các tệp tin XML phức tạp. Với JSF, nhiều thứ thực hiện nhanh hơn so với các khung công tác Web Java khác.
### Về hướng dẫn này
Về hướng dẫn này
[Phần 1](http://www.ibm.com/developerworks/vn/edu/j-jsf1/) đã để lại. Nếu bạn
là người mới bắt đầu với JSF, hoặc chỉ muốn ôn lại, thì hãy đọc bài đã
đăng đầu tiên trước khi bạn bắt đầu phần này. Thậm chí nếu bạn là một
người chuyên nghiệp về JSF già dặn, có một hay hai khả năng được đánh
giá cao thì điều đó cũng sẽ giúp bạn.
Mặc dù hỗ trợ công cụ là một lợi ích chính của JSF, bạn sẽ không sử dụng các công cụ ưa thích hay sự hỗ trợ của IDE trong hướng dẫn này. Hướng dẫn này đề cập những điểm cốt yếu, với thông tin nền tảng để tiếp tục trình bày và để giữ cho bạn cách học tập có hiệu quả việc sử dụng JSF để xây dựng các ứng dụng Web.
### Các mục tiêu
Trong hướng dẫn này sẽ: tiếp tục nhìn tổng quan về các tính năng của JSF và học cách làm việc với tất cả các thành phần JSF; xây dựng một ứng dụng đơn giản quản lý các mối giao tiếp — một danh sách liệt kê CRUD cơ bản (tạo, đọc, cập nhật, xóa); sau khi tìm hiểu về vòng đời của ứng dụng JSF, cải tiến ứng dụng này với các trình chuyển đổi (converter) và các trình duyệt tính hợp lệ (validator). Hướng dẫn này uốn khúc với một chút hương vị về lập trình JSF cao cấp: tạo một khung công tác duyệt tính hợp lệ mức đối tượng bằng cách sử dụng một trình nghe pha.
### Ai nên tìm hiểu hướng dẫn này?
Nếu bạn là người mới bắt đầu tìm hiểu JSF, hướng dẫn này để dành cho bạn. Ngay cả khi bạn đã sử dụng JSF nhưng chưa thử nghiệm các tính năng JSF 1.2 hoặc chỉ sử dụng các công cụ GUI để xây dựng các ứng dụng JSF, bạn sẽ có khả năng học hỏi được rất nhiều từ cả hai hướng dẫn trong loạt bài này.
### Các yêu cầu tiên quyết
Hướng dẫn này được viết cho các nhà phát triển Java có kinh nghiệm ở mức bắt đầu tới mức trung cấp. Bạn cần phải có hiểu biết chung về cách sử dụng ngôn ngữ Java, cùng với một số kinh nghiệm phát triển giao diện đồ họa người dùng (GUI).
### Các yêu cầu hệ thống
Để chạy các ví dụ trong hướng dẫn này, bạn cần có một môi trường phát
triển Java (JDK) và Maven Apache. Nó giúp để có một IDE Java. Các tệp
tin dự án Maven và các tệp tin dự án trong Eclipse Java EE và Web
Tools Project (WTP) được cung cấp. Xem
[Tải
về](#download) để nhận được mã ví dụ. Hãy truy cập vào trang web sách
hướng dẫn của tác giả (xem [Tài nguyên](#resources)) để
biết thêm thông tin về cách làm thế nào để chạy các ví dụ này.
## Ứng dụng CRUD JSF mẫu
Phần này giới thiệu một ứng dụng CRUD đơn giản mà bạn sẽ xây dựng trong các phần tiếp sau để tìm hiểu về:
- Từng thành phần HTML tiêu chuẩn của JSF
- Tạo các trình biến đổi tùy biến
- Làm việc với các trình duyệt tính hợp lệ
- Làm việc với các trình nghe pha
### Một ứng dụng quản lý các mối giao tiếp
Ứng dụng mà bạn sẽ xây dựng
trong phần này là một ứng dụng quản lý giao tiếp có cấu trúc giống như
ứng dụng máy tính bỏ túi trong
[Phần 1](http://www.ibm.com/developerworks/vn/edu/j-jsf1/index.html). Như bạn có thể
thấy trong [Hình 1](#fig1), ứng dụng này là một danh sách
CRUD tiêu chuẩn. Nó không yêu cầu các quy tắc dẫn hướng do toàn bộ ứng
dụng sử dụng chỉ một khung nhìn đơn (trang contacts.jsp).
##### Hình 1. Ứng dụng mẫu quản lý các mối giao tiếp
![ Ứng dụng mẫu quản lý các mối giao tiếp](figure1.jpg)
[Hình 2](#fig2) Cho thấy lưu đồ cơ sở của ứng dụng:
##### Hình 2. Ứng dụng mẫu quản lý các mối giao tiếp, sơ đồ liên kết
![ Ứng dụng mẫu quản lý các mối giao tiếp, sơ đồ liên kết](figure2.gif)
Ứng dụng CRUD này bao gồm các phần tử sau đây:
ContactController: Trình điều khiển JSF
Contact: Đối tượng mô hình để mô tả thông tin về các mối giao tiếp
ContactRepository: Đối tượng mô hình để tạo, đọc, cập nhật, và xóa các đối tượng
Contact
- contacts.jsp: Trang JavaServer Pages (JSP) để bố trí cây thành phần JSF quản lý các giao tiếp
- faces-config.xml: Cấu hình JSF, nơi bạn khai báo
ContactControllervà
ContactRepositorynhư là các bean quản lý và nội xạ
ContactRepositoryvà
ContactController
### Trình điều kiển giao tiếp (ContactController)
ContactController hậu thuẫn trang
contacts.jsp.
[Listing 1](#listing1) hiển thị mã cho
ContactController:
##### Listing 1.
ContactController
package com.arcmind.contact.controller; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.component.UICommand; import javax.faces.component.UIForm; import javax.faces.context.FacesContext; import com.arcmind.contact.model.Contact; import com.arcmind.contact.model.ContactRepository; public class ContactController { /** Contact Controller collaborates with contactRepository. */ private ContactRepository contactRepository; /** The current contact that is being edited. */ private Contact contact = new Contact(); /** Contact to remove. */ private Contact selectedContact; /** The current form. */ private UIForm form; /** Add new link. */ private UICommand addNewCommand; /** Persist command. */ private UICommand persistCommand; /** For injection of collaborator. */ public void setContactRepository(ContactRepository contactRepository) { this.contactRepository = contactRepository; } public void addNew() { form.setRendered(true); addNewCommand.setRendered(false); persistCommand.setValue("Add"); } public void persist() { form.setRendered(false); addNewCommand.setRendered(true); if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact); } else { addStatusMessage("Updated " + contact); } } public void remove() { contactRepository.remove(selectedContact); addStatusMessage("Removed " + selectedContact); } public void read() { contact = selectedContact; form.setRendered(true); addNewCommand.setRendered(false); addStatusMessage("Read " + contact); persistCommand.setValue("Update"); } private void addStatusMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, null)); } //most getter/setter omitted }
Listing
1 tạo ra một GUI CRUD với chưa đến 74 dòng mã — không
quá tệ có phải không.
ContactController được
quản lý trong phạm vi,
yêu cầu , do đó một
Contact mới được tạo ra khi một
ContactController được khởi tạo. Ba thành
phần —
biểu mẫu (form) (có kiểu là
UIForm),
addNewCommand (có kiểu là
UICommand), và
persistCommand
(có kiểu là
UICommand) —
được liên kết với
ContactController.
Phương thức
addNew() đảm bảo
rằng:
biểu mẫu (form)được bật lên để người sử dụng có thể nhập vào một giao tiếp mới —
form.setRendered(true)
addNewCommandđược tắt đi —
addNewCommand.setRendered(false)
- nhãn của
persistCommandđược đặt là
Add—
persistCommand.setValue("Add")
Phương thức
persist() vừa xử lý cập
nhật mối giao tiếp hiện có và vừa xử lý bổ sung thêm mối giao tiếp mới
bằng cách sử dụng
contactRepository. Phương
thức
persist() tắt
form bật
addNewCommand. Phương
thức
remove() loại bỏ mối giao tiếp ra khỏi
hệ thống bằng cách sử dụng
contactRepository.
Phương thức
read() sao chép
selectedContact
(có kiểu là
Contact) vào một
mối giao tiếp. (
Mối giao
tiếp — cũng có kiểu là
Contact— có giá trị liên kết với
biểu
mẫu). Bạn có thể tự hỏi
selectedContact bắt nguồn từ đâu. Nó được chọn khi người
dùng nhấn vào một mối giao tiếp trong danh sách các mối giao tiếp.
(Hướng dẫn này sẽ đề cập khi thảo luận về JSP.) Như trong
[Phần 1](http://www.ibm.com/developerworks/vn/edu/j-jsf1/),
addStatusMessage bổ sung thêm các thông báo
trạng thái để bạn có thể hiển thị chúng với
<h:messages>.
### Khung nhìn các mối giao tiếp
Trang JSP sử
dụng một
<h:dataTable> (một thành
phần không được giới thiệu trong Phần 1), như hiển thị trong Listing
2:
##### Listing 2. contacts.jsp
<?xml version="1.0" encoding="ISO-8859-1" ?> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">   <title>Contacts</title> <link rel="stylesheet" type="text/css" href="<%=request.getContextPath()%>/css/main.css" />   <f:view>
Contacts
 <h:messages infoClass="infoClass" errorClass="errorClass" layout="table" globalOnly="true" /> <h:form> <h:commandLink binding="#{contactController.addNewCommand}" action="#{contactController.addNew}" value="Add New..." /> </h:form> <h:form binding="#{contactController.form}" rendered="false" styleClass="form"> <h:inputHidden value="#{contactController.contact.id}" /> <h:panelGrid columns="6"> <%-- First Name --%> <h:outputLabel value="First Name" for="firstName" accesskey="f" /> <h:inputText id="firstName" label="First Name" required="true" value="#{contactController.contact.firstName}" size="10" /> <h:message for="firstName" errorClass="errorClass" /> <%-- Last Name --%> <h:outputLabel value="Last Name" for="lastName" accesskey="l" /> <h:inputText id="lastName" required="true" value="#{contactController.contact.lastName}" size="15" /> <h:message for="lastName" errorClass="errorClass" /> </h:panelGrid> <h:panelGroup> <h:commandButton binding="#{contactController.persistCommand}" action="#{contactController.persist}" /> </h:panelGroup> </h:form> <h:form> <h:dataTable value="#{contactController.contacts}" var="contact" rowClasses="oddRow, evenRow" rendered="#{not empty contactController.contacts}" styleClass="contactTable" headerClass="headerTable" columnClasses="normal,centered"> <h:column> <f:facet name="header"> <h:column> <h:outputText value="Name" /> </h:column> </f:facet> <h:outputText value="#{contact.lastName}, #{contact.firstName}" /> </h:column> <h:column> <f:facet name="header"> <h:column> <h:outputText value="Action" /> </h:column> </f:facet> <h:panelGrid columns="2"> <h:commandLink value="remove" action="#{contactController.remove}"> <f:setPropertyActionListener target="#{contactController.selectedContact}" value="#{contact}" /> </h:commandLink> <h:commandLink value="edit" action="#{contactController.read}"> <f:setPropertyActionListener target="#{contactController.selectedContact}" value="#{contact}" /> </h:commandLink> </h:panelGrid> </h:column> </h:dataTable> </h:form> </f:view>
<h:dataTable> là giá trị được liên
kết để hiển thị các mối giao tiếp từ
contactController, khi sử dụng
"#{contactController.contacts}". Mỗi mối giao tiếp được
ánh xạ vào bảng bằng cách sử dụng thuộc tính:
var="contact". Trong contacts.jsp, các kiểu dáng (style)
oddRow và
evenRow (được định nghĩa trong một tệp tin CSS) được đặt
vào trong thuộc tính
rowClasses với
rowClasses="oddRow, evenRow". Điều này cho
phép
<h:dataTable> sử dụng các kiểu
dáng luân phiên cho các hàng trong bảng. Hãy kiểm tra tài liệu hướng
dẫn trực tuyến
<h:dataTable> để tìm
một danh sách đầy đủ về những cái có thể làm với
<h:dataTable> và các kiểu dáng, bởi vì
<h:dataTable> có khá nhiều khả năng.
(Xem
[Tài nguyên](#resources) để tìm liên kết đến JSF
API Javadocs).
Bạn cũng có thể thiết lập một kiểu dáng cho toàn
bộ
<h:dataTable> với
styleClass="contactTable" hoặc cho vùng tiêu
đề của nó với
headerClass="headerTable".
Hoặc bạn có thể thực hiện các kiểu dáng luân phiên tương tự như thế
cho các cột bằng cách sử dụng thuộc tính
columnClasses:
columnClasses="normal,centered".
<h:dataTable> được thiết lập để nó không hoàn trả
nếu không có mối giao tiếp:
rendered="#{not empty
contactController.contacts}"nào. Thành phần
<h:dataTable> rất linh hoạt và dễ sử
dụng.
Bên trong
<h:dataTable>, bạn sử dụng các
<h:column> để hiển thị các giá trị theo các thuộc
tính. Mỗi cột định nghĩa một phần tiêu đề trong một thẻ
<f:facet>. Một
facet là một thành phần tên mà các thành phần khác sử
dụng. Vậy thì sau
<f:facet> và bên
trong thành phần
<h:column> bạn hãy
sử dụng thành phần
<h:outputText> để
đưa ra các thuộc tính
firstName và
lastName.
Mỗi hàng có một liên kết
loại bỏ và một liên kết chỉnh sửa, mỗi liên kết sử dụng một
<h:commandLink>.
commandLink loại bỏ được liên kết với phương thức
contactController.remove. Liên kết chỉnh sửa
được liên kết với phương thức
contactController.read . Thuộc tính
contactController.selectedContact được điền bằng hàng hiện
tại theo cách bạn cấu hình
<f:setPropertyActionListener>.
<f:setPropertyActionListener> làm cho mối giao tiếp
của hàng hiện tại được sao chép vào
selectedContact trước khi phương thức hành động được gọi.
### Tệp faces-config.xml cho ứng dụng CRUD của các mối giao tiếp
Tệp tin
faces-config.xml nối
ContactRepository vào
trong
ContactController, như hiển thị trong
Listing 3:
##### Listing 3. faces-config.xml
<managed-bean> <managed-bean-name>contactRepository</managed-bean-name> <managed-bean-class> com.arcmind.contact.model.ContactRepository </managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>contactController</managed-bean-name> <managed-bean-class> com.arcmind.contact.controller.ContactController </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>contactRepository</property-name> <property-class> com.arcmind.contact.model.ContactRepository </property-class> <value>#{contactRepository}</value> </managed-property> </managed-bean>
Lưu ý rằng
contactRepository nằm trong phạm
vi
ứng dụng và nó được nội xạ vào trong
contactRepository của
contactController bằng cách sử dụng phần tử
<managed-property>. Kỹ thuật này
cho phép bạn nội xạ các phụ thuộc/các trình cộng tác vào trong trình
điều khiển để hỗ trợ việc duy trì mô hình và khung nhìn riêng biệt, và
nó cho phép bạn nội xạ các đối tượng giả để sau này có thể được thay
thế bằng các đối tượng thực. Nhiều hơn một lần, tôi đã làm giả một đối
tượng mô hình như
ContactRepository và sau
đó thay thế nó bằng phiên bản thực sau khi hoàn thành GUI.
Mô
hình của ứng dụng là khá đơn giản, như hiển thị trong các Listings 4
và 5. Listing 4 hiển thị lớp
Contact:
##### Listing 4.
Contact
package com.arcmind.contact.model; public class Contact { private String firstName; private String lastName; protected long id; public Contact(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public Contact() { } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (id ^ (id >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Contact other = (Contact) obj; if (id != other.id) return false; return true; } @Override public String toString() { return String.format("Contact: %s %s", firstName, lastName); } public long getId() { return id; } public void setId(long id) { this.id = id; } }
Listing
5 hiển thị lớp
ContactRepository, để mô
phỏng việc viết các mối giao tiếp vào một cơ sở dữ
liệu:
##### Listing 5.
ContactRepository
package com.arcmind.contact.model; import java.util.LinkedHashMap; import java.util.List; import java.util.ArrayList; import java.util.Map; public class ContactRepository { private Map<Long, Contact> contacts = new LinkedHashMap<Long, Contact>(); private static long counter = 1l; public List<Contact> getContacts() { return new ArrayList<Contact>(contacts.values()); } public synchronized Contact persist(Contact contact) { if (contact.id == 0) { contact.id = counter++; } return contacts.put(contact.id, contact); } public synchronized void remove(Contact contact) { contacts.remove(contact.id); } }
Những gì bạn có bây giờ là một ứng dụng CRUD khá cơ bản. Trong phần kế tiếp, bạn sẽ xây dựng bên trên cơ sở này để tìm hiểu cách làm thế nào để sử dụng các thành phần JSF khác nhau.
## Làm việc với các thành phần JSF
Trong phần này, bạn sẽ sử dụng nhiều thành phần JSF để nâng cao ứng dụng CRUD:
<f:subview>
<h:selectOneMenu>
<h:selectOneRadio>
<h:selectBooleanCheckbox>
<h:selectManyCheckbox>
<h:inputTextarea>
Hình 3 cho thấy các thành phần này xuất hiện trong GUI như thế nào:
##### Hình 3. Quản lý các mối giao tiếp với một số lượng lớn các thành phần JSF phổ biến
![ Quản lý các mối giao tiếp với một số lượng lớn các thành phần JSF phổ biến](figure3.jpg)
### Các khung nhìn phụ
Như bạn có thể hình dung, thật khó để bao gồm tất cả các thành phần
JSF của bạn trên một trang web. May mắn thay, bạn có thể phân chia các
thành phần JSF theo các khung nhìn riêng biệt bằng cách sử dụng một
<f:subview>, như hiển thị trong
Listing 6:
##### Listing 6. Các khung nhìn phụ contacts.jsp
 <f:view>
Contacts (2nd version)
 <h:messages infoClass="infoClass" errorClass="errorClass" layout="table" globalOnly="true" /> <h:form> <h:commandLink binding="#{contactController.addNewCommand}" action="#{contactController.addNew}" value="Add New..." /> </h:form> <f:subview id="form"> <jsp:include page="form.jsp" /> </f:subview> <f:subview id="listing"> <jsp:include page="listing.jsp" /> </f:subview> </f:view>
Bạn có thể sử dụng các
<f:subview>
trong trang cha mẹ hay trong trang bao gồm (chứ không phải cả hai).
Trong JSF 1.2
<f:subview> là tùy
chọn. Trong các phiên bản JSF cũ hơn, nó là bắt buộc. Một số các IDE
hình như cũng chờ đợi các
<f:subview>, do đó bạn có thể cần sử dụng chúng ngay cả
khi bạn đang sử dụng JSF 1.2 hoặc phiên bản mới hơn.
### Chọn một
Trong JSF, các
thành phần được tách thành hai phần: thành phần JSF chính nó và một
trình hoàn trả có trách nhiệm hiển thị thành phần đó. Thành phần
UISelectOne có một số trình hoàn trả. Nó hậu
thuẫn
HtmlSelectOneListbox,
HtmlSelectOneMenu, và
HtmlSelectOneRadio.
Ứng dụng quản lý các mối giao
tiếp (phiên bản thứ 2) sử dụng
<h:selectOneMenu>. Để điều này hoạt động, bạn bổ
sung thêm ba đối tượng mô hình mới:
Group
(chỉ ra trong
[Listing 7](#listing7)),
Thẻ (Tag), và
ContactType. Bạn
cũng thêm hai đối tượng kho lưu trữ mới:
GroupRepository và
TagRepository, tương tự như
ContactRepository.
ContactType
không cần có một kho lưu trữ bởi vì nó là một
Bảng
kê (Enum). Lớp
Contact hiện nay
có ba thuộc tính mới dành cho nhóm có nó ở trong đó (được gọi là
group), dành cho các thẻ có kết hợp với nó
(
tags), và cuối cùng là dành cho kiểu
của nó
type.
##### Listing 7. Các khung nhìn phụ contacts.jsp/form.jsp
<%-- Group --%> <h:outputLabel value="Group" for="group" accesskey="g" /> <h:selectOneMenu id="group" validatorMessage="required" value="#{contactController.selectedGroupId}"> <f:selectItems value="#{contactController.groups}" /> <f:validateLongRange minimum="1" /> </h:selectOneMenu> <h:message for="group" errorClass="errorClass" />
Lưu ý rằng
selectOneMenu sử dụng thuộc tính
giá trị để liên kết
selectOneMenu với
selectedGroupId. Phần thân của
selectOneMenu chứa một
<f:selectItems>, đó là giá trị liên kết với thuộc
tính
các nhóm:
value=#{contactController.groups}. Bạn tạo ra một danh
sách các nhóm trong bean phía sau. Mã cho thuộc tính
selectedGroupId và thuộc tính
các nhóm có trong Listing 8:
##### Listing 8. Xây dựng một danh sách của các nhóm
public class ContactController { ... private GroupRepository groupRepository; ... private Long selectedGroupId; ... public List<SelectItem> getGroups() { List<Group> groups = groupRepository.list(); List<SelectItem> list = new ArrayList<SelectItem>(groups.size()+1); list.add(new SelectItem(Long.valueOf(-1L), "select one")); for (Group group : groups) { SelectItem selectItem = new SelectItem(group.getId(), group.getName()); list.add(selectItem); } return list; } //Other getter/setters removed ...
Thuộc
tính
các nhóm (groups) trả về một danh sách
các
SelectItem. Lớp
SelectItem được sử dụng để biểu diễn một mục trong một
danh sách. Nó được cả hai thành phần
UISelectMany và
UISelectOne sử
dụng. Lưu ý rằng phương thức
getGroups sử
dụng
groupRepository, đây là một đối tượng
kho lưu trữ, được nội xạ như
contactRepository để nhận một danh sách các nhóm. The
groupRepository quản lý các đối tượng
Group . Một
Group đại diện cho một nhóm. Một
Contact đại diện một mối giao tiếp. Phương thức
getGroups() tạo ra một danh sách các
SelectItem bằng cách sử dụng thuộc tính
group.id như là một giá trị và thuộc tính
group.name như là một nhãn.
Lưu
ý rằng bạn thêm một
SelectItem "chọn một"
với giá trị
là -1. Bạn sử dụng giá trị này
để xác định một mục có được lựa chọn không. Nếu nó không được lựa
chọn, bạn chọn mục chưa được chọn bằng cách sử dụng
<f:validateLongRange minimum="1" /> ở
trong
selectOneMenu (xem lại
[Listing 7](#listing7)). Cũng cần chú ý rằng
selectOneMenu sử dụng
validatorMessage="required" để hiển thị một thông báo lỗi
ngắn.
Chú ý rằng
[Listing 8](#listing8) gọi trực
tiếp kho lưu trữ. Nếu kho lưu trữ đã thực sự đang trao đổi với một cơ
sở dữ liệu hoặc bộ nhớ cache, bạn muốn thực hiện lời gọi đến phương
thức hành động để thực hiện xử lý lỗi ở đó và phơi bày ra các kết quả
như các mục đã chọn trong thuộc tính.
Khi biểu mẫu (form) được
gửi đi
selectedGroupId được thiết lập.
Phương thức
persist() được liên kết với nút
cập nhật và nút tạo ra, sẽ sử dụng
selectedGroupId để tìm kiếm nhóm trong kho lưu trữ, như
hiển thị trong Listing 9:
##### Listing 9. Cập nhật
persist() để sử dụng
selectedGroupId
public class ContactController { ... public void persist() { /* Setup the group into contact. */ contact.setGroup(groupRepository.lookup(selectedGroupId)); /* Turn form off, turn link on. */ form.setRendered(false); addNewCommand.setRendered(true); /* Add a status message. */ if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact); } else { addStatusMessage("Updated " + contact); } } ... public void read() { /* Prepare selected contact. */ contact = selectedContact; /* Turn form on and the link off. */ form.setRendered(true); addNewCommand.setRendered(false); /* Prepare selected group id. */ selectedGroupId = contact.getGroup().getId(); ... this.selectedTagIds = tagIds.toArray(new Long[tags.size()]); addStatusMessage("Read " + contact); persistCommand.setValue("Update"); } ...
Lưu
ý rằng lớp
Contact bổ sung thêm một thuộc
tính
nhóm. Vì vậy
Contact có mối quan hệ nhiều-với-một với
Group.
Phương thức
read() trong
[Listing 9](#listing9) khởi tạo
this.selectedTagIds sao cho nó hiển thị
dưới dạng đã được chọn trong khung nhìn.
Bạn đã thấy cách bạn
có thể xử lý một mối quan hệ một-một với
<h:selectOne> như thế nào.
Contact cũng có một mối quan hệ nhiều-với-nhiều với các
Thẻ (Tags), đươc biểu diễn bằng thuộc
tính
tags của
Contact. Để xử lý điều này, hãy sử dụng một
<h:selectManyCheckbox>, như hiển thị
trong Listing 10:
##### Listing 10.
selectManyCheckbox cho
Contact.tags
<h:selectManyCheckbox id="tags" value="#{contactController.selectedTagIds}"> <f:selectItems value="#{contactController.availableTags}" /> </h:selectManyCheckbox>
Sự khác biệt chính với
<h:selectManyCheckbox> là ở chỗ nó được liên kết với
một mảng của các
số nguyên dài, thay vì một
số nguyên
số nguyên dài, như hiển thị trong
Listing 10.
Mã Java tương tự như
[Listing
8](#listing8), ngoại trừ bây giờ bạn đang làm việc với một mảng các ID
được chọn các (
số nguyên dài), thay cho một
số nguyên dài, như hiển thị trong
Listing 11:
##### Listing 11. Mã Java để hậu thuẫn
selectManyCheckbox
public class ContactController { ... private Long[] selectedTagIds; private TagRepository tagRepository; ... public List<SelectItem> getAvailableTags() { List<Tag> tags = tagRepository.list(); List<SelectItem> list = new ArrayList<SelectItem>(tags.size()); for (Tag tag : tags) { SelectItem selectItem = new SelectItem(tag.getId(), tag.getName()); list.add(selectItem); } return list; } ... public void persist() { ... /* Setup the tags into contact. */ List<Tag> tags = new ArrayList<Tag>(selectedTagIds.length); for (Long selectedTagId : selectedTagIds) { tags.add(tagRepository.lookup(selectedTagId)); } contact.setTags(tags); ... } ... public void read() { ... /* Prepare selected tag IDs. */ List<Tag> tags = contact.getTags(); List<Long> tagIds = new ArrayList<Long>(tags.size()); for (Tag tag : tags) { tagIds.add(tag.getId()); } this.selectedTagIds = tagIds.toArray(new Long[tags.size()]); ... } ...
Phương thức
persist() sử dụng phương thức
tagRepository.lookup() để tìm kiếm các
đối tượng kiểu
Thẻ (Tag) dựa trên các
selectedTagId, và sau đó đặt thuộc tính
contact.tags bằng với các giá trị đã tìm
thấy. Phương thức
read() khởi tạo các
selectedTagId dựa trên cơ sở các
Thẻ (Tags) có trong thuộc tính
contact.tags.
Lớp
UISelectMany hậu thuẫn
<h:selectManyCheckbox>, nhưng nó cũng hậu thuẫn
<h:selectManyListbox> và
<h:selectManyMenu>. Bạn có thể sử dụng
chúng thay cho
<h:selectManyCheckbox>, như hiển thị trong Listings 12
và 13, và Hình 4.
##### Listing 12.
<selectManyListbox>
<h:selectManyListbox id="tags" value="#{contactController.selectedTagIds}"> <f:selectItems value="#{contactController.availableTags}"/> </h:selectManyListbox>
##### Listing 13.
<selectManyMenu>
<h:selectManyMenu id="tags" value="#{contactController.selectedTagIds}"> <f:selectItems value="#{contactController.availableTags}"/> </h:selectManyMenu>
##### Hình 4:
<selectManyListbox> và
<selectManyMenu>
![ Hình 4. selectManyCheckbox, selectManyListbox và selectManyMenu](figure4.gif)
Lưu ý rằng thiết lập cho tất cả ba thẻ là như nhau.
JSF 1.2 có
các trình biến đổi cho các
Enum, do đó, bạn
không cần phải tìm kiếm các giá trị. Bạn có thể sử dụng chúng trực
tiếp và liên kết với chúng mà không cần có một thuộc tính trung gian
trong trình điều khiển giống như bạn đã dành cho
các thẻ và
nhóm (xem
[Listing 8](#listing8) và [Listing
11](#listing11)). Lớp
Contact có một thuộc tính
enum, như hiển thị trong Listing 14:
##### Listing 14.
Contact sử dụng
enum cho thuộc tính
kiểu (type)
public enum ContactType { BUSINESS, PERSONAL; public String toString () { return this.name().toLowerCase(); } } ... public class Contact implements Serializable { ... private Group group; private List<Tag> tags; ... private ContactType type = ContactType.PERSONAL; ... public ContactType getType() { return type; } public void setType(ContactType type) { this.type = type; } ... }
Vì JSF có một trình biến đổi cho các
Enum,
bạn có thể liên kết trực tiếp vào thuộc tính
type, như hiển thị trong Listing 15:
##### Listing 15. Liên kết trực tiếp vào
contact.type
<h:selectOneRadio id="type" value="#{contactController.contact.type}"> <f:selectItem itemValue="PERSONAL" itemLabel="personal" /> <f:selectItem itemValue="BUSINESS" itemLabel="business" /> </h:selectOneRadio>
Listing 15 cũng giải thích việc sử dụng
<f:selectItem>, nó tạo ra các giá trị đơn. Trình
biến đổi kèm sẵn của JSF cho các
Enum chờ
đợi đầu vào là các giá trị chuỗi ký tự — thực sự đó là
các tên trong các
Enum. Bạn cũng có thể sử
dụng một
<h:selectOneListbox>, như
hiển thị trong Listing 16:
##### Listing 16. Sử dụng
<h:selectOneListbox>
<h:selectOneListbox id="type" value="#{contactController.contact.type}"> <f:selectItem itemValue="PERSONAL" itemLabel="personal"/> <f:selectItem itemValue="BUSINESS" itemLabel="business"/> </h:selectOneListbox>
### Các thành phần khác của ứng dụng quản lý các mối giao tiếp
Ứng dụng CRUD quản lý các mối
giao tiếp cũng giải thích việc sử dụng
<h:selectBooleanCheckbox> và
<h:inputTextarea>, như hiển thị trong Listing 17:
##### Listing 17. Sử dụng
<h:selectBooleanCheckbox> and
<h:textArea>
<h:inputHidden value="#{contactController.contact.id}" /> ... <%-- active --%> <h:outputLabel value="Active" for="active" accesskey="a" /> <h:selectBooleanCheckbox id="active" value="#{contactController.contact.active}" /> <h:message for="active" errorClass="errorClass" /> <%-- Description --%> ... <h:outputLabel value="Description" for="description" accesskey="d" style="font: large;" /> <h:inputTextarea id="description" cols="80" rows="5" value="#{contactController.contact.description}" /> <h:message for="description" errorClass="errorClass" />
<h:inputTextarea> có hai thuộc tính
bổ sung được đặt với
cols="80" rows="5".
Các liên kết hoạt động như trước.
Listing 18 cho thấy các thuộc tính mà bạn liên kết tới trong Listing 17:
##### Listing 18. Lớp
Contact
public class Contact implements Serializable { ... private String description; private boolean active; protected long id; ... public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public long getId() { return id; } public void setId(long id) { this.id = id; } ... }
Bạn liên kết các thuộc tính mã nhận dạng id
mô
tả (description),
kiểu (type),
tên (firstName), và
họ (lastName) của
Contact trực
tiếp với UI. Các thuộc tính
nhóm (group) và
các thẻ (tags) có thể không được liên
kết trực tiếp bởi vì chúng không có ("
[trình
biến đổi dữ liệu JSF](#)"). Trong một phần sau của hướng dẫn này
("Các trình biến đổi dữ liệu JSF"- JSF data converters), bạn sẽ tìm
hiểu về các trình biến đổi và tạo ra một số cái cho ứng dụng này. Giữa
bây giờ và sau này, hãy tham gia một hành trình ngắn bên lề đi vào
vòng đời của ứng dụng JSF.
## Vòng đời của ứng dụng JSF
Bạn có thể (trái với suy nghĩ chung) viết các ứng dụng JSF mà không hiểu biết mọi chi tiết nhỏ về cách thức hoạt động của công nghệ này; bạn có thể học được rất nhiều chỉ bằng cách tự mình kiếm một dự án và ráp nối lại thông qua nó. Tuy nhiên, sự hiểu biết các nguyên tắc cơ bản nhất định sẽ làm cho các nỗ lực phát triển của bạn được đền đáp nhiều hơn — và tiêu tốn ít thời gian hơn. Phần này vòng ra ngoài, cách xa khỏi ứng dụng quản lý các mối giao tiếp và đưa bạn dạo qua sáu pha trong vòng đời yêu cầu – xử lý của JSF. Hãy xem xét những gì xảy ra trong mỗi pha và các pha liên kết với nhau như thế nào. Cuộc thảo luận này sẽ thông báo công việc bạn sẽ làm trong phần còn lại của hướng dẫn này.
### Các pha của vòng đời của ứng dụng JSF
Sáu pha trong vòng đời của ứng dụng JSF là:
[Khôi phục khung nhìn](#phase1) [Áp dụng các giá trị yêu cầu; xử lý các sự kiện](#phase2) [Xử lý duyệt tính hợp lệ; xử lý các sự kiện](#phase3) [Cập nhật các giá trị của mô hình; xử lý các sự kiện](#phase4) [Gọi ứng dụng; xử lý các sự kiện](#phase5) [Hoàn trả đáp ứng](#phase6)
Sáu pha xuất hiện theo trình tự mà JSF xử lý tiêu biểu một biểu mẫu (form) GUI. Danh sách hiển thị các pha theo thứ tự thực hiện của chúng với việc xử lý sự kiện ở mỗi pha, nhưng vòng đời JSF hầu như không được thiết lập cứng nhắc như vậy. Bạn có thể thay đổi thứ tự thực hiện bằng cách bỏ qua các pha hay bỏ hẳn vòng đời. Ví dụ, nếu một giá trị yêu cầu không hợp lệ được sao chép vào một thành phần, khung nhìn hiện tại sẽ được hiển thị lại, và một số pha có thể không thực hiện được.
Bạn cũng có thể chọn bỏ hẳn JSF và khi đó có lẽ
giao việc cho servlet hay một khung công tác ứng dụng khác. Trong
trường hợp này, bạn có thể phát ra một lời gọi phương thức
FacesContext.responseComplete để chuyển hướng
người dùng đến một trang Web hoặc nguồn tài nguyên Web khác, sau đó sử
dụng bộ điều vận (dispatcher) yêu cầu (lấy ra từ đối tượng yêu cầu
trong
FacesContext) để chuyển tiếp đến một
nguồn tài nguyên Web thích hợp. Ngoài ra, bạn cũng có thể gọi
FacesContext.renderResponse để hoàn trả lại
khung nhìn gốc.
Vấn đề là cho phép vòng đời ứng dụng cấu trúc các nỗ lực phát triển của bạn nhưng không cảm thấy hoàn toàn bị buộc vào nó. Bạn có thể thay đổi vòng đời mặc định khi cần mà không lo sợ làm đổ vỡ ứng dụng của bạn. Trong hầu hết trường hợp, bạn sẽ nhận thấy rằng vòng đời JSF đáng được tuân theo vì nó khá logic.
Các biểu mẫu phải được duyệt tính hợp lệ trước khi có thể thực hiện bất kỳ logic ứng dụng nào, và dữ liệu của trường phải được biến đổi trước khi duyệt tính hợp lệ. Việc bám sát vòng đời giải phóng cho bạn, để tập trung suy nghĩ về các chi tiết trong duyệt tính hợp lệ và biến đổi, hơn là nghĩ về chính các pha của quá trình xử lý yêu cầu. Cũng rất quan trọng khi lưu ý rằng các khung công tác Web khác cũng có vòng đời tương tự; chỉ có điều không được quảng cáo hay giải thích tốt.
Một số nhà phát triển khi sử dụng JSF có thể chẳng bao giờ viết một thành phần hoặc mở rộng một khung công tác trong khi những người khác chỉ tập trung vào các nhiệm vụ đó. Mặc dù vòng đời của JSF sẽ giống nhau với hầu hết các dự án, bạn sẽ có khả năng đề cập đến nó ở các giai đoạn khác nhau dựa trên vai trò của bạn trong dự án. Nếu bạn đang tập trung nhiều hơn về phát triển ứng dụng tổng thể, có nhiều khả năng bạn sẽ quan tâm đến các pha giữa của chu kỳ xử lý yêu cầu:
- Áp dụng các giá trị yêu cầu
- Xử lý duyệt tính hợp lệ
- Cập nhật các giá trị mô hình
- Gọi ứng dụng
Nếu bạn đang tập trung vào việc phát triển thành phần JSF, nhiều khả năng bạn sẽ tập trung vào các pha đầu tiên và cuối cùng của vòng đời:
- Khôi phục khung nhìn
- Hoàn trả đáp ứng
Trong phần còn lại của phần này, bạn sẽ đi qua từng giai đoạn của chu kỳ xử lý yêu cầu của JSF, bao gồm cả xử lý sự kiện và duyệt tính hợp lệ. Trước khi bạn bắt đầu, hãy xem Hình 5, một sơ đồ vòng đời của ứng dụng JSF để có cái nhìn tổng thể:
##### Hình 5. Vòng đời của ứng dụng JSF
![ Vòng đời của JSF](figure5.jpg)
### Pha 1: Khôi phục khung nhìn
Trong pha đầu của vòng đời JSF —
*khôi phục khung nhìn*— một yêu cầu đi qua
FacesServlet. Servlet xem xét yêu cầu và trích ra mã nhận
dạng ID của khung nhìn, được xác định bởi tên của trang JSP.
Trình điều khiển của khung công tác JSF sử dụng mã nhận dạng khung nhìn để tìm kiếm các thành phần dùng cho khung nhìn hiện tại. Nếu khung nhìn còn chưa tồn tại, trình điều khiển JSF tạo ra nó. Nếu khung nhìn đã tồn tại, trình điều khiển JSF sử dụng nó. Khung nhìn có chứa tất cả các thành phần GUI.
Pha này của vòng đời đưa ra ba cá
thể khung nhìn: khung nhìn mới, khung nhìn khởi đầu, và khung nhìn
hiện lại (postback), mỗi khung nhìn được xử lý khác nhau. Trong trường
hợp khung nhìn mới, JSF xây dựng khung nhìn của trang Faces và nối các
trình xử lý sự kiện và trình duyệt tính hợp lệ tới các thành phần.
Khung nhìn được lưu trong một đối tượng
FacesContext.
FacesContext lưu trữ các thông tin trạng
thái mà JSF cần để quản lý trạng thái của thành phần GUI cho yêu cầu
hiện tại.
FacesContext lưu khung nhìn trong
thuộc tính
viewRoot của nó;
viewRoot chứa tất cả các thành phần JSF của
khung nhìn hiện tại.
Trong trường hợp của khung nhìn khởi đầu (lần đầu tiên một trang web được nạp), JSF tạo ra một khung nhìn rỗng. Khung nhìn rỗng được điền dần dần khi trang JSP được xử lý. Từ khung nhìn khởi đầu, JSF tiến thẳng đến pha hoàn trả đáp ứng.
Trong trường hợp khung nhìn hiện lại (postback - người dùng quay lại một trang mà anh/chị ấy đã truy cập trước đó), khung nhìn tương ứng với trang này đã tồn tại, do đó nó chỉ cần được khôi phục lại. Trong trường hợp này, JSF sử dụng thông tin trạng thái của khung nhìn hiện có để xây dựng lại trạng thái của nó.
### Pha 2: Áp dụng các giá trị yêu cầu
Mục đích của pha
*áp dụng các giá trị yêu cầu *
là để cho mỗi thành phần lấy ra trạng thái hiện tại của nó. Các thành
phần trước tiên phải được lấy ra hoặc được tạo ra từ đối tượng
FacesContext theo sau là các giá trị của
chúng. Các giá trị thành phần thường được lấy ra từ các tham số yêu
cầu, mặc dù chúng cũng có thể được lấy ra từ cookie hoặc các tiêu đề.
Giá trị từ tham số yêu cầu cho nhiều thành phần được lưu trong
submittedValue của thành phần.
Nếu
một thuộc tính xử lý-sự kiện tức thời của thành phần được đặt là true
(đúng), các giá trị sẽ được biến đổi sang kiểu đúng và được duyệt tính
hợp lệ (pha sau sẽ nói nhiều hơn về phép biến đổi). Sau đó, giá trị đã
biến đổi được lưu trong thành phần. Nếu việc biến đổi giá trị hoặc
việc duyệt tính hợp lệ thất bại, một thông báo lỗi được tạo ra và xếp
hàng đợi trong
FacesContext, nơi nó sẽ được
hiển thị trong pha hoàn trả đáp ứng, cùng với bất kỳ lỗi duyệt tính
hợp lệ nào khác.
### Pha 3: Xử lý duyệt tính hợp lệ
Phép biến đổi và duyệt
tính hợp lệ thông thường xảy ra trong pha
*xử lý duyệt tính hợp lệ
*. Thành phần biến đổi
submittedValue
của nó và lưu trữ giá trị. Vì vậy, nếu trường này được liên kết với
một thuộc tính
Số nguyên (Integer) (ví dụ
thế), thì giá trị được biến đổi thành
Số
nguyên. Nếu phép biến đổi giá trị không thành công, một
thông báo lỗi được tạo ra và xếp hàng đợi trong
FacesContext, nơi nó sẽ được hiển thị trong pha hoàn trả
đáp ứng, cùng với bất kỳ lỗi duyệt tính hợp lệ nào khác.
Việc
xử lý sự kiện đầu tiên của vòng đời diễn ra sau pha áp dụng các giá
trị yêu cầu. Ở giai đoạn này, các giá trị của mỗi thành phần được
duyệt tính hợp lệ theo các quy tắc duyệt tính hợp lệ của ứng dụng. Các
quy tắc duyệt tính hợp lệ có thể được định nghĩa trước (được gửi đi
cùng với JSF), hoặc được các nhà phát triển xác định. Các giá trị do
người sử dụng nhập vào được so với các quy tắc duyệt tính hợp lệ. Nếu
một giá trị nhập vào không hợp lệ, một thông báo lỗi sẽ được thêm vào
FacesContext, và thành phần này được
đánh dấu là không hợp lệ. Nếu một thành phần được đánh dấu là không
hợp lệ, JSF chuyển tiếp đến pha hoàn trả đáp ứng và bỏ qua các pha còn
lại. Nó sẽ hiển thị khung nhìn hiện tại với các thông báo lỗi duyệt
tính hợp lệ. Nếu không có lỗi duyệt tính hợp lệ nào xảy ra, JSF tiến
đến pha cập nhật các giá trị của mô hình.
### Pha 4: Cập nhật các giá trị của mô hình
Pha thứ tư của vòng đời ứng dụng JSF —
* cập nhật các giá trị của mô hình *— cập nhật các giá trị thực tại của mô hình phía máy
chủ bằng cách cập nhật các thuộc tính của bean quản lý của bạn. Chỉ có
các thuộc tính của bean bị liên kết với một giá trị của thành phần
được cập nhật. Lưu ý rằng pha này xảy ra sau khi duyệt tính hợp lệ, vì
vậy bạn có thể chắc chắn rằng các giá trị được sao chép vào các thuộc
tính các bean của bạn là hợp lệ (ít nhất là ở mức trường biểu mẫu;
chúng có thể vẫn còn không hợp lệ ở mức quy tắc nghiệp vụ).
### Pha 5: Gọi ứng dụng
Tại pha thứ năm của vòng đời —
* gọi ứng dụng *— trình điều khiển của JSF gọi ứng dụng để xử lý đệ
trình biểu mẫu. Các giá trị thành phần sẽ được biến đổi, được duyệt
tính hợp lệ, và được áp dụng cho các đối tượng của mô hình, do đó bây
giờ bạn có thể sử dụng chúng để thi hành logic nghiệp vụ của ứng dụng.
Chính là trong pha này các phương thức của trình xử lý-hành
động của bạn được gọi, ví dụ như các phương thức
persist() và
read() trong
ContactController của ứng dụng mẫu.
Ở
pha này, bạn cũng bắt đầu phải chỉ rõ khung nhìn logic tiếp theo đối
với một diễn tiến đã cho hoặc một số diễn tiến có khả năng xảy ra. Bạn
làm điều này bằng cách định nghĩa một kết quả cụ thể cho một đệ trình
biểu mẫu thành công và trả về kết quả đó. Ví dụ:
*khi kết quả đúng,
di chuyển người dùng đến trang tiếp theo*. Để dẫn hướng này
hoạt động, bạn phải tạo ra một ánh xạ tới kết quả thành công đó làm
quy tắc dẫn hướng trong tệp tin faces-config.xml. Sau khi chuyển hướng
xảy ra, bạn tiến tới pha cuối cùng của vòng đời. JSF nhận đối tượng
trả về từ phương thức hoạt động và gọi phương thức
toString() của nó. Sau đó, nó sử dụng giá trị này như là
kết quả cho các quy tắc dẫn hướng. (
[Phần 1](http://www.ibm.com/developerworks/vn/edu/j-jsf1/index.html) đã đề cập cấu hình
các quy tắc dẫn hướng.)
### Pha 6: Hoàn trả đáp ứng
Trong pha thứ sáu
của vòng đời —
* hoàn trả đáp ứng *— bạn hiển thị khung nhìn với tất cả các thành phần của
nó trong trạng thái hiện tại của chúng.
Hình 6 là một sơ đồ trạng thái đối tượng của sáu pha của vòng đời ứng dụng JSF bao gồm việc duyệt tính hợp lệ và xử lý sự kiện:
##### Hình 6. Sự tiến triển sáu-pha của vòng đời ứng dụng JSF
![ Sự tiến triển sáu-pha của vòng đời ứng dụng JSF](figure6.jpg)
## Các trình biến đổi dữ liệu JSF
Phép biến đổi là một quá trình đảm bảo dữ liệu của đúng đối tượng
hoặc đúng kiểu, do đó bạn biến đổi các giá trị chuỗi ký tự thành các
kiểu khác như các đối tượng
Ngày tháng
(Date), các đối tượng số
Dấu phẩy động
(Float) nguyên thủy, hoặc số
Dấu phảy động
(Float). Bạn có thể sử dụng các trình biến đổi kèm sẵn hoặc viết trình
biến đổi tùy biến theo nhu cầu. Phần này cho bạn thấy các trình biến
đổi tiêu chuẩn của JSF và sau đó trình bày chi tiết về viết các trình
biến đổi tùy biến.
### Trình biến đổi tiêu chuẩn của JSF
JSF cung cấp nhiều trình biến đổi dữ liệu tiêu chuẩn, và hầu hết các phép biến đổi dữ liệu sẽ diễn ra tự động. Bảng 1 cho thấy các mã nhận dạng ID của trình biến đổi và các lớp thực thi tương ứng được JSF sử dụng cho các phép biến đổi dữ liệu đơn giản.
##### Các trình biến đổi tiêu chuẩn của JSF
|Trình biến đổi||Lớp thực thi|
Vì vậy, nếu bạn liên kết tới một
int
hoặc một
Số nguyên (Integer), phép biến đổi
sẽ diễn ra tự động. Listing 19 cho thấy một thành phần của ứng dụng
mẫu quản lý các mối giao tiếp liên kết trực tiếp đến
age.
#{contactController.contact.age}:
##### Listing 19. Liên kết tới
age: JSF biến đổi tự động
<%-- age --%> <h:outputLabel value="Age" for="age" accesskey="age" /> <h:inputText id="age" size="3" value="#{contactController.contact.age}"> </h:inputText>
JSF thực hiện điều này cho tất cả các thuộc tính có kiểu nguyên thủy,
kiểu bao gói,
kiểu chuỗi ký tự (String), và
Enum. Thật tuyệt. Nó cũng có các trình
biến đổi cho ngày tháng cũng như các số. Các số có thể có nhiều định
dạng, do đó, nó có một trình biến đổi cho phép bạn mô tả định dạng mà
người sử dụng cuối sẽ dùng. Cũng như thế cho các ngày tháng. Listing
20 cho thấy việc sử dụng các trình biến đổi của JSF để biến đổi một
ngày tháng bằng cách sử dụng một định dạng tùy biến:
Mặc dù JSF
xử lý các kiểu nguyên thủy và gần nguyên thủy khá tốt theo mặc định,
khi làm việc với dữ liệu ngày tháng, bạn phải chỉ rõ thẻ biến đổi
<f:convertDateTime/>. Thẻ này dựa
vào gói
java.text và sử dụng các mẫu ngắn,
dài và tùy biến.
[Listing 20](#listing20) giải thích cách
sử dụng
<f:convertDateTime/> như thế
nào để bảo đảm ngày tháng năm sinh của người sử dụng biến đổi được
thành một đối tượng ngày tháng, được định dạng là
*MM/yyyy*
( *tháng/năm*). Xem tài liệu hướng dẫn API Java để tìm
java.text.SimpleDateFormat với một danh sách
các mẫu (xem
[Tài nguyên](#resources)).
##### Listing 20. Chỉ rõ định dạng cho ngày tháng
<%-- birthDate --%> <h:outputLabel value="Birth Date" for="birthDate" accesskey="b" /> <h:inputText id="birthDate" value="#{contactController.contact.birthDate}"> <f:convertDateTime pattern="MM/yyyy"/> </h:inputText> <h:message for="birthDate" errorClass="errorClass" />
### Giới thiệu các trình biến đổi tùy biến của JSF
Trình biến đổi dữ liệu tùy biến là cần thiết nếu bạn cần biến đổi dữ liệu vào trong một đối tượng giá trị đặc thù của ứng dụng, như trong ví dụ sau:
Chuỗi ký tựthành
PhoneNumberobject (
PhoneNumber.areaCode,
PhoneNumber.prefix, ...)
Chuỗi ký tựthành đối tượng
Name(
Name.first,
Name.last)
Chuỗi ký tựthành đối tượng
ProductCodeobject (
ProductCode.PartNum,
ProductCode.rev, ...)
Chuỗi ký tựthành đối tượng
Group
Chuỗi ký tựthành đối tượng
Tags
Để tạo một trình biến đổi tùy biến, bạn cần phải:
- Thực hiện giao diện
trình biến đổi(còn được gọi là
javax.faxes.convert.Converter).
- Thực hiện phương thức
getAsObject(), trong đó biến đổi một trường (chuỗi ký tự) thành một đối tượng (ví dụ
PhoneNumber).
- Thực hiện phương thức
getAsString, trong đó biến đổi một đối tượng (ví dụ,
PhoneNumber) thành một chuỗi ký tự.
- Đăng ký các trình biến đổi tùy biến của bạn vào bối cảnh
Faces.
Bạn có thể xem trong hình 7 các bước này khớp với vòng đời của ứng dụng JSF như thế nào:
##### Hình 7. Các phương
thức
getAsObject() và
getAsString() của trình biến đổi tùy
biến
![ Biến đổi phướng thức getAsObject và getAsString](figure7.jpg)
Trong Hình 7, JSF gọi phương thức
getAsObject() của trình biến đổi tùy biến trong pha tiến
hành duyệt tính hợp lệ. Đây là nơi trình biến đổi phải biến đổi giá
trị chuỗi ký tự yêu cầu thành kiểu đối tượng mong muốn và sau đó trả
về đối tượng để lưu trữ trong thành phần JSF tương ứng. Khi giá trị
được hoàn trả trở lại trong khung nhìn, JSF sẽ gọi phương thức
getAsString trong pha hoàn trả đáp ứng. Điều
này có nghĩa là trình biến đổi cũng chịu trách nhiệm chuyển dữ liệu
đối tượng trở lại thành một biểu diễn chuỗi ký tự .
### Thực hiện giao diện của
Trình biến đổi
Nhắc lại rằng đối tượng miền
Contact của ứng dụng mẫu có mối quan hệ nhiều-với-một với
Group và mối quan hệ nhiều-với-nhiều
với
Tag. Trước đó (xem
[Listing 9](#listing9) và [Listing 11](#listing11)) bạn đã thực
hiện phép biến đổi từ giá trị
id thành các
đối tượng miền trong
ContactController.
Thay vì liên kết trực tiếp vào các thuộc tính miền trong khung nhìn,
bạn đã liên kết tới trường
id trong
ContactController. Nếu bạn sử dụng các trình
biến đổi JSF, bạn có thể loại bỏ rất nhiều đoạn mã kiểu này và làm đơn
giản trình điều khiển và khung nhìn.
Listings 21 và 22 thực
hiện giao diện
Converter cho các trình biến
đổi
Group và
Tag, tương ứng:
##### Listing 21. Trình biến đổi
Group thực hiện giao diện
Converter
package com.arcmind.contact.converter; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.application.FacesMessage; import com.arcmind.contact.model.Group; import com.arcmind.contact.model.GroupRepository; public class GroupConverter implements Converter { ... }
##### Listing 22. Trình biến đổi
Thẻ (Tag) thực hiện giao diện
Converter
package com.arcmind.contact.converter; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import com.arcmind.contact.model.Tag; import com.arcmind.contact.model.TagRepository; public class TagConverter implements Converter { ... }
### Thực hiện phương thức
getAsObject()
Bước kế tiếp là thực hiện phương thức
getAsObject() biến đổi một trường (chuỗi ký tự) thành một
đối tượng. Listing 23 cho thấy phương thức
getAsObject() cho
GroupConverter:
##### Listing 23. Phương thức
getAsObject() cho
GroupConverter
... public class GroupConverter implements Converter { public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { GroupRepository repo = (GroupRepository) facesContext .getExternalContext().getApplicationMap() .get("groupRepository"); Long id = Long.valueOf(value); if (id == -1L) { throw new ConverterException(new FacesMessage( FacesMessage.SEVERITY_ERROR, "required", "required")); } return repo.lookup(id); } ... }
Lưu
ý rằng
GroupConverter tìm kiếm
groupRepository và sử dụng
groupRepository để đọc
Group từ kho lưu trữ. Cũng cần chú ý rằng nó kiểm tra để
xem giá trị có là
-1L không, và nếu đúng
thế, nó gửi một báo lỗi bắt buộc bằng cách đưa ra một
ConverterException mới.
TagConverter cũng làm tương tự. Nó sử dụng
tagRepository để tìm kiếm giá trị thẻ.
Listing 24 cho thấy phương thức
getAsObject() cho
TagConverter:
##### Listing 24. Phương thức
getAsObject() cho
TagConverter
... public class TagConverter implements Converter { public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { TagRepository repo = (TagRepository) facesContext .getExternalContext().getApplicationMap() .get("tagRepository"); return repo.lookup(Long.valueOf(value)); } ... }
Không
trình biến đổi nào thực hiện kiểm tra lỗi. Nếu đối tượng kho lưu trữ
của bạn thực tại đang trao đổi với máy chủ cơ sở dữ liệu hoặc cache,
bạn có thể muốn bao bọc phương thức
getAsObject() trong một khối lệnh
try/
catch, và thêm một
FacesMessage với mức nghiêm trọng là
SEVERITY_FATAL nếu có một vấn đề xảy ra với
cơ sở dữ liệu — tương tự như cách bạn xử lý trường hợp
-1L/
bắt buộc
báo lỗi
GroupConverter như trong
[Listing 23](#listing23).
### Thực hiện phương thức
getAsString
JSF cần hiển thị giá trị được chọn hiện tại. Nó thực hiện
điều này bằng cách gọi phương thức
getAsString() của
Trình biến
đổi (Converter). Listing 25 thực hiện phương thức
getAsString() cho
GroupConverter:
##### Listing 25. Phương thức
getAsString() cho
GroupConverter
... public class GroupConverter implements Converter { ... public String getAsString(FacesContext facesContext, UIComponent component, Object value) { return value == null ? "-1" : "" + ((Group) value).getId(); } }
Listing
26 Thực hiện phương thức
getAsString() cho
TagConverter:
##### Listing 26. Phương thức
getAsString() cho
TagConverter
... public class TagConverter implements Converter { ... public String getAsString(FacesContext facesContext, UIComponent component, Object value) { return value == null ? "-1" : "" + ((Tag) value).getId(); } }
### Đăng ký trình biến đổi tùy biến của
bạn trong bối cảnh
Faces
Sau khi bạn viết các trình biến đổi của mình, bạn cần JSF sử
dụng chúng mỗi khi nó thấy một giá trị liên kết sinh ra trong một
Group hay
Tag. Bạn thực hiện điều này bằng cách đăng ký các trình
biến đổi tùy biến của bạn trong tệp tin Faces-config.xml, khi sử dụng
phần tử
<converter>, như hiển thị
trong Listing 27:
##### Listing 27. Đăng ký các trình biến đổi với faces-config.xml
<converter> <converter-for-class> com.arcmind.contact.model.Group </converter-for-class> <converter-class> com.arcmind.contact.converter.GroupConverter </converter-class> </converter> <converter> <converter-for-class> com.arcmind.contact.model.Tag </converter-for-class> <converter-class> com.arcmind.contact.converter.TagConverter </converter-class> </converter>
Listing
27 chỉ rõ lớp trình biến đổi ứng với phần tử
<converter-class> và lớp được bạn cung cấp phép biến
đổi cho nó, ứng với phần tử
<converter-for-class>.
### Làm cho các trình biến đổi làm việc với một danh sách các thẻ
Không may là, các trình biến đổi không
làm việc với các mẫu tổng quát (generics) như là
List<Tag>. JSF không cho phép biến đổi thành các
danh sách mẫu tổng quát. (Nó lẽ ra phải cho phép. Các API lâu bền Java
[JPA] không có vấn đề gì khi xử lý
List<Tag> để định nghĩa các mối quan hệ. JPA và JSF
1.2 được xuất bản cùng thời gian với Java EE 5, do đó bạn đã nghĩ rằng
cả hai sẽ hỗ trợ mẫu tổng quát). Để khắc phục vấn đề này, bạn có thể
sử dụng các mảng. Listing 28 cho thấy việc sử dụng một mảng
Tag, thay cho
List<Tag>:
##### Listing 28. Sử dụng các mảng thay cho các mẫu tổng quát
public class Contact implements Serializable { ... private List<Tag> tags; public Tag[] getTags() { if (tags != null) { return tags.toArray(new Tag[tags.size()]); } else { return null; } } public void setTags(Tag[] tags) { this.tags = Arrays.asList(tags); }
## Các trình duyệt tính hợp lệ của JSF
Mục đích chính của phép biến đổi và phép duyệt tính hợp lệ là đảm bảo các giá trị đã được làm sạch hoàn toàn trước khi cập nhật dữ liệu của mô hình. Rồi sau đó, khi đến lúc gọi các phương thức của ứng dụng để làm một cái gì đó với dữ liệu, bạn có thể yên tâm đặt một số giả định nào đó về trạng thái của mô hình. Phép biến đổi và phép duyệt tính hợp lệ cho phép bạn tập trung vào logic nghiệp vụ hơn là vào các tiêu chuẩn tẻ nhạt về dữ liệu đủ tư cách cho đầu vào như kiểm tra rỗng, chiều dài của các từ hạn định, các giới hạn phạm vi và v.v.
Vậy thì điều quan trọng là các quá trình biến đổi và duyệt tính hợp lệ
xảy ra trước khi dữ liệu thành phần được liên kết vào bean quản lý của
bạn trong pha cập nhật dữ liệu mô hình của vòng đời. Như bạn đã thấy
trong phần "
[Vòng đời của ứng dụng JSF](#)",
phép biến đổi và phép duyệt tính hợp lệ xảy ra trong pha xử lý duyệt
tính hợp lệ — đầu tiên là phép biến đổi, sau đó là phép
duyệt tính hợp lệ.
Có bốn dạng duyệt tính hợp lệ trong JSF:
[Các thành phần duyệt tính hợp lệ kèm sẵn](#form1) [Duyệt tính hợp lệ mức-ứng dụng](#form2) [Phương thức duyệt tính hợp lệ trong các bean hậu thuẫn (nội tuyến)](#form3) [Các thành phần duyệt tính hợp lệ tùy biến (để thực hiện giao diện](#form4)
Trình duyệt tính hợp lệ-Validator))
Phần này giải thích và biểu diễn từng dạng duyệt tính hợp lệ này.
### Phép duyệt tính hợp lệ tiêu chuẩn
JSF cung cấp ba thành phần duyệt tính hợp lệ tiêu chuẩn:
DoubleRangeValidator: Giá trị tại chỗ của thành phần phải là kiểu số; phải nằm trong phạm vi đã ấn định bởi các giá trị nhỏ nhất, các giá trị lớn nhất, hoặc cả hai.
LongRangeValidator: Giá trị tại chỗ của thành phần phải là kiểu số và có thể biến đổi thành
số nguyên dài; phải nằm trong phạm vi đã ấn định bởi các giá trị nhỏ nhất, các giá trị lớn nhất, hoặc cả hai.
LengthValidator: Kiểu phải là
chuỗi ký tự; chiều dài phải nằm trong phạm vi đã ấn định bởi các giá trị nhỏ nhất, các giá trị lớn nhất, hoặc cả hai.
Trong ví dụ ứng dụng này, tuổi của một cá nhân cần giao tiếp có thể là
bất kỳ số nguyên nào hợp lệ. Bởi vì sẽ không có ý nghĩa gì nếu cho
phép tuổi là -2, có lẽ bạn sẽ muốn thêm một phép duyệt tính hợp lệ cho
trường này. Liệt kê 29 cho thấy một số mã lệnh duyệt tính hợp lệ đơn
giản khi sử dụng
<f:validateLongRange> để đảm bảo tính toàn vẹn dữ
liệu của mô hình trong một trường tuổi:
##### Listing 29. Duyệt tính hợp lệ cho tuổi với các giá trị hợp lý, sử dụng
<f:validateLongRange>
<%-- age --%> <h:outputLabel value="Age" for="age" accesskey="age" /> <h:inputText id="age" size="3" value="#{contactController.contact.age}"> <f:validateLongRange minimum="0" maximum="150"/> </h:inputText> <h:message for="age" errorClass="errorClass" />
Một khi bạn đã giải quyết được trường tuổi, bạn có thể muốn xác định các giới hạn chiều dài cho trường First Name, như hiển thị trong Listing 30.
##### Listing 30. Đảm bảo chắc chắn
firstName không quá dài hay quá ngắn
<%-- First Name --%> <h:outputLabel value="First Name" for="firstName" accesskey="f" /> <h:inputText id="firstName" label="First Name" required="true" value="#{contactController.contact.firstName}" size="10" > <f:validateLength minimum="2" maximum="25" /> </h:inputText> <h:message for="firstName" errorClass="errorClass" />
Mặc dù nó hoạt động với nhiều kịch bản, nhưng phép duyệt tính hợp lệ
kèm sẵn của JSF cũng bị hạn chế một chút. Nếu bạn đang làm việc với
phép duyệt tính hợp lệ của e-mail, các số điện thoại, các URL, các
ngày tháng, và v.v, đôi khi tốt hơn là viết riêng trình duyệt tính hợp
lệ của bạn; vấn đề này sẽ được thảo luận sau trong phần này. Bạn cũng
có thể sử dụng các trình duyệt tính hợp lệ được Tomahawk, Shale,
JSF-validations, và Crank cung cấp (xem
[Tài
nguyên](#resources)).
### Phép duyệt tính hợp lệ mức ứng dụng
Theo khái niệm, phép duyệt tính hợp lệ mức-ứng dụng thực sự là phép duyệt tính hợp lệ logic nghiệp vụ. JSF tách phép duyệt tính hợp lệ mức biểu mẫu hay mức trường dữ liệu khỏi phép duyệt tính hợp lệ logic nghiệp vụ. Về cơ bản, phép duyệt tính hợp lệ mức ứng dụng đòi hỏi phải bổ sung thêm mã vào các phương thức bean quản lý, sử dụng mô hình để xác định tiêu chuẩn của dữ liệu đã liên kết tới mô hình của bạn. Trong trường hợp của một xe đẩy mua hàng trong siêu thị, phép duyệt tính hợp lệ mức biểu mẫu có thể xác nhận số lượng món hàng đã nhập vào biểu mẫu có hợp lệ không, nhưng bạn sẽ cần có phép duyệt tính hợp lệ mức logic kinh doanh để kiểm tra xem người mua hàng đã vượt quá giới hạn tín dụng của mình hay không. Đây là một ví dụ khác về sự tách biệt các mối quan tâm trong JSF.
Ví dụ, giả sử rằng người dùng nhấp vào một nút bấm được liên kết với
một phương thức hành động, mà sẽ được gọi trong pha gọi ứng dụng (xem
lại
[Hình 5](#fig5) để biết thêm chi tiết). Trước khi thực
hiện bất kỳ thao tác dữ liệu nào, mà dữ liệu có lẽ đã được cập nhật
trong pha cập nhật dữ liệu mô hình, bạn có thể thêm mã để kiểm tra xem
dữ liệu nhập vào có hợp lệ không, dựa vào các quy tắc nghiệp vụ của
ứng dụng.
Chẳng hạn, trong ví dụ ứng dụng, người sử dụng nhấn chuột vào nút
Update/Add đã được liên kết với phương thức
persist() của trình điều khiển ứng dụng. Bạn có thể thêm
mã duyệt tính hợp lệ vào phương thức
persist() để xác định xem liệu cặp
firstName/
lastName đã có trong
hệ thống chưa. Trong trường hợp mối giao tiếp này đã tồn tại, bạn cũng
có thể thêm một thông báo đến
FacesContext
và sau đó trực tiếp tới JSF để lưu lại khung nhìn hiện tại bằng cách
trả về
không (null) làm kết quả (nếu các
quy tắc dẫn hướng áp dụng cho hành động này).
Chúng ta hãy xem lại ứng dụng quản lý các mối giao tiếp và thực hiện
một số logic mức ứng dụng trong phương thức hành động
persist(), như hiển thị trong Listings 31 và
32. Listing 31 hiển thị logic duyệt tính hợp lệ mức ứng dụng trong
trình điều khiển:
##### Listing 31. Logic duyệt tính hợp lệ mức ứng dụng trong trình điều khiển
public class ContactController { public String persist() { /* Perform the application level validation. */ try { contact.validate(); } catch (ContactValidationException contactValidationException) { addErrorMessage(contactValidationException.getLocalizedMessage()); return null; } /* Turn form off, turn link on. */ form.setRendered(false); addNewCommand.setRendered(true); /* Add a status message. */ if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact); } else { addStatusMessage("Updated " + contact); } return "contactPersisted"; } private void addErrorMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( FacesMessage.SEVERITY_ERROR, message, null)); }
Trong Listing 31, bạn có thể thấy rằng phương thức
persist() gọi
validate() trên
đối tượng
mối giao tiếp. Nó bắt giữ bất kỳ
trường hợp ngoại lệ nào và biến đổi thông báo lỗi trường hợp ngoại lệ
thành một
FacesMessage. Nếu một trường hợp
ngoại lệ xảy ra, nó trả về một kết quả
null, có nghĩa là
*lưu lại khung nhìn hiện tại và không dẫn
hướng tới khung nhìn tiếp theo*.
Mã duyệt tính hợp lệ thực có trong mô hình — đó là
phương thức
validate() của lớp
Contact, như hiển thị trong Listing 32. Điều
này rất quan trọng: khi bạn thêm quy tắc duyệt tính hợp lệ vào lớp
Contact, bạn không cần phải thay đổi trình điều khiển hoặc các tầng
khung nhìn.
##### Listing 32. Duyệt tính hợp lệ trong mô hình, chứ không phải trong trình điều khiển
... public class Contact implements Serializable { ... public void validate() throws ContactValidationException { if ( (homePhoneNumber == null || "".equals(homePhoneNumber)) && (workPhoneNumber == null || "".equals(workPhoneNumber)) && (mobilePhoneNumber == null || "".equals(mobilePhoneNumber)) ) { throw new ContactValidationException("At least one phone number" + "must be set"); } }
Việc duyệt tính hợp lệ mức ứng dụng rõ ràng là trực tiếp và dễ dàng thực hiện. Lợi thế của nó là:
- Dễ thực hiện
- Không cần có một lớp tách riêng (trình duyệt tính hợp lệ tùy biến)
- Không cần biên tập trang để chỉ rõ trình duyệt tính hợp lệ
Những nhược điểm của việc duyệt tính hợp lệ mức ứng dụng là ở chỗ nó xảy ra sau các hình thức duyệt tính hợp lệ khác (tiêu chuẩn, tuỳ biến và thành phần), và các thông báo lỗi chỉ hiển thị sau khi các hình thức duyệt tính hợp lệ khác đã xảy ra.
Cuối cùng, việc duyệt tính hợp lệ mức ứng dụng nên được sử dụng chỉ cho các trường hợp yêu cầu duyệt tính hợp lệ logic nghiệp vụ.
### Trình duyệt tính hợp lệ tùy biến trong các bean phía sau
Bạn cần phải xây dựng các thành phần duyệt tính hợp lệ tùy biến riêng của bạn cho các kiểu dữ liệu không được hỗ trợ bởi các trình duyệt tính hợp lệ JSF tiêu chuẩn, bao gồm cả các địa chỉ e-mail và mã ZIP. Bạn cũng cần phải xây dựng trình duyệt tính hợp lệ riêng của bạn trong trường hợp bạn muốn kiểm soát rõ ràng các thông báo duyệt tính hợp lệ được hiển thị cho người dùng cuối. Với JSF, bạn có thể tạo các thành phần duyệt tính hợp lệ cắm chạy được và sử dụng lại trong toàn bộ các ứng dụng web của bạn.
Như một cách thay thế cho việc tạo ra một lớp duyệt tính hợp lệ riêng biệt, bạn có thể chỉ cần thực hiện duyệt tính hợp lệ tuỳ biến theo yêu cầu trong phương thức bean phía sau. Điều này là tốt hơn cho các nhà phát triển ứng dụng. Ví dụ, bạn có thể viết một phương thức trong một bean quản lý để duyệt tính hợp lệ các số điện thoại, như hiển thị trong Listing 33:
##### Listing 33. Duyệt tính hợp lệ của các số điện thoại (Phone-number)
public class ContactValidators { private static Pattern phoneMask; static { String countryCode = "^[0-9]{1,2}"; String areaCode = "(|-|\\(){1,2}[0-9]{3}(|-|\\)){1,2}"; String prefix = "(|-)?[0-9]{3}"; String number = "(|-)[0-9]{4}$"; phoneMask = Pattern.compile(countryCode + areaCode + prefix + number); } public void validatePhone(FacesContext context, UIComponent component, Object value) throws ValidatorException { String sValue = (String)value; Matcher matcher = phoneMask.matcher(sValue); if (!matcher.matches()) { FacesMessage message = new FacesMessage(); message.setDetail("Phone number not valid"); message.setSummary("Phone number not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } } ... //ADD MORE VALIDATION METHODS FOR THE APP HERE! }
Lớp
ContactValidators có một phương thức
validatePhone(). Phương thức
validatePhone() sử dụng API
các biểu thức chính quy của Java (API regex
Java) để đảm bảo chắc chắn rằng chuỗi ký tự đã nhập vào là một số điện
thoại hợp lệ. Nếu giá trị không khớp với mẫu, thì phương thức
validatePhone() đưa ra một
ValidatorException.
Để sử dụng lớp
ContactValidators, bạn đăng
ký nó trong faces-config.xml file, như hiển thị trong Listing 34:
##### Listing 34. Đăng ký
ContactValidators như là một bean quản lý
<managed-bean> <managed-bean-name>contactValidators</managed-bean-name> <managed-bean-class>com.arcmind.contact.validators.ContactValidators</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean>
Để sử dụng trình duyệt tính hợp lệ, bạn dùng thuộc tính
validator, như hiển thị trong Listing 35, cho
các mục công việc (work), địa chỉ nhà ở (home) và số điện thoại di
động (mobile phone-number):
##### Listing 35. Sử dụng trình duyệt tính hợp lệ trong khung nhìn với thuộc tính
validator
<%-- Work --%> <h:outputLabel value="Work" for="work" accesskey="w" /> <h:inputText id="work" value="#{contactController.contact.workPhoneNumber}" size="11" validator="#{contactValidators.validatePhone}" /> <h:message for="work" errorClass="errorClass" /> <%-- Home --%> <h:outputLabel value="Home" for="home" accesskey="h" /> <h:inputText id="home" value="#{contactController.contact.homePhoneNumber}" size="11" validator="#{contactValidators.validatePhone}" /> <h:message for="home" errorClass="errorClass" /> <%-- Mobile --%> <h:outputLabel value="Mobile" for="mobile" accesskey="m" /> <h:inputText id="mobile" value="#{contactController.contact.mobilePhoneNumber}" size="11" validator="#{contactValidators.validatePhone}" /> <h:message for="mobile" errorClass="errorClass" />
Như bạn có thể thấy, bạn liên kết phương thức
validatePhone() với thành phần
<h:inputText>:
<h:inputText
id="mobile" ...
validator="#{contactValidators.validatePhone}".
Việc sử dụng một bean quản lý để duyệt tính hợp lệ là tốt cho các nhà phát triển ứng dụng. Tuy nhiên, nếu bạn đang phát triển một khung công tác có thể sử dụng lại được hoặc một tập các thành phần có thể sử dụng lại được, sẽ là tốt hơn khi tạo các trình duyệt tính hợp lệ tùy biến độc lập.
### Trình duyệt tính hợp lệ tùy biến độc lập
Với JSF, bạn có thể tạo các thành phần duyệt tính hợp lệ có khả năng cắm chạy được và sử dụng lại trong suốt các ứng dụng web của bạn.
Để tạo một trình duyệt tính hợp lệ, bạn thực hiện theo các bước sau:
- Tạo ra một lớp mà sẽ thực hiện giao diện
Validator(
javax.faces.validator.Validator).
- Thực hiện phương thức
validate().
- Đăng ký trình duyệt tính hợp lệ tùy biến của bạn trong tệp tin faces-config.xml.
- Sử dụng thẻ
<f:validator/>trong tệp JSP của bạn.
Chúng ta hãy nhìn vào từng bước một trong các bước ở trên, bao gồm ví dụ mã để tạo một trình duyệt tính hợp lệ tùy biến theo yêu cầu.
#### Bước 1: Thực hiện giao diện
Validator
Bước đầu tiên là thực hiện giao diện
Validator, như hiển thị trong Listing 36:
##### Listing 36. Thực hiện giao diện
Validator
package com.arcmind.validators; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ZipCodeValidator implements Validator { /** Accepts zip codes like 85710 */ private static final String ZIP_REGEX = "[0-9]{5}"; /** Optionally accepts a plus 4 */ private static final String PLUS4_OPTIONAL_REGEX = "([ |-]{1}[0-9]{4})?"; private static Pattern mask = null; static { mask = Pattern.compile(ZIP_REGEX + PLUS4_OPTIONAL_REGEX); }
#### Bước 2: Thực hiện phương thức
validate()
Tiếp theo, bạn cần phải thực hiện phương thức
validate(), như hiển thị trong Listing 37:
##### Listing 37. Thực hiện phương thức
validate()
public class ZipCodeValidator implements Validator { ... public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { /* Get the string value of the current field */ String zipField = (String) value; /* Check to see if the value is a zip code */ Matcher matcher = mask.matcher(zipField); if (!matcher.matches()) { FacesMessage message = new FacesMessage(); message.setDetail("Zip code not valid"); message.setSummary("Zip code not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } } }
#### Bước 3: Đăng ký trình duyệt tính hợp lệ tùy biến của bạn
Mã để đăng ký trình duyệt tính hợp lệ tùy biến với
FacesContext, như hiển thị trong Listing 38, bây giờ trông
quen thuộc với bạn:
##### Listing 38. Đăng ký trình duyệt tính hợp lệ tùy biến của bạn trong faces-config.xml
<validator> <validator-id>arcmind.zipCode</validator-id> <validator-class>com.arcmind.validators.ZipCodeValidator</validator-class> </validator>
#### Bước 4: Sử dụng thẻ
<f:validator> trong tệp JPS của
bạn
Thẻ
<f:validator/> khai báo việc sử
dụng trình duyệt tính hợp lệ
zipCode, như
hiển thị trong Listing 39:
##### Listing 39. Sử dụng thẻ
<f:validator> trong tệp JSP của bạn
<%-- zip --%> <h:outputLabel value="Zip" for="zip" accesskey="zip" /> <h:inputText id="zip" size="5" value="#{contactController.contact.zip}"> <f:validator validatorId="arcmind.zipCode"/> </h:inputText> <h:message for="zip" errorClass="errorClass" />
Nhìn chung, việc tạo các trình duyệt tính hợp lệ tùy biến khá đơn giản và làm cho việc duyệt tính hợp lệ có thể sử dụng lại trên nhiều ứng dụng. Mặt tiêu cực là bạn cần phải tạo một lớp khác và quản lý việc đăng ký trình duyệt tính hợp lệ trong bối cảnh faces. Tuy nhiên, bạn có thể làm cho việc triển khai thực hiện trình duyệt tính hợp lệ tuỳ biến của bạn tiến thêm một bước nữa và làm cho nó trông giống như phép duyệt tính hợp lệ kèm sẵn bằng cách tạo ra một thẻ tùy biến để sử dụng trình duyệt tính hợp lệ này. Với các trường hợp duyệt tính hợp lệ được quan tâm chung như của e-mail, cách tiếp cận này có thể hỗ trợ triết lý thiết kế, khi mà sử dụng lại mã và hành vi nhất quán của ứng dụng là quan trọng nhất.
### Quay lại chủ đề phép duyệt tính hợp lệ và phép biến đổi
Khi bạn bắt đầu tới pha duyệt tính hợp lệ, việc biến đổi đã xảy ra. Ví
dụ, nếu bạn có một thuộc tính
int được liên
kết vào một trường
inputText, thì trường
này sẽ được biến đổi trước khi bạn bắt đầu duyệt tính hợp lệ của nó.
Giả sử bạn có một đối tượng giá trị
PhoneNumber, và thay vì sử dụng các
Chuỗi ký tự (String) để lưu các số điện thoại trong
Contact, bạn sử dụng
PhoneNumber. Thế thì quy tắc duyệt tính hợp lệ trong
[Listing 33](#listing33) cho các số điện thoại sẽ không có
nhiều ý nghĩa. Thực vậy, quy tắc duyệt tính hợp lệ ấy chỉ chứng tỏ
rằng
String đã có định dạng của một số điện
thoại. Logic đó thực sự nên là một phần của trình biến đổi, như
Listing 40 đã chứng tỏ:
##### Listing 40. Quay lại chủ đề phép biến đổi và phép duyệt tính hợp lệ:
PhoneConverter
package com.arcmind.contact.converter; import java.util.regex.Pattern; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import com.arcmind.contact.model.PhoneNumber; /** * @author Richard Hightower * */ public class PhoneConverter implements Converter { private static Pattern phoneMask; static { String countryCode = "^[0-9]{1,2}"; String areaCode = "( |-|\\(){1,2}[0-9]{3}( |-|\\)){1,2}"; String prefix = "( |-)?[0-9]{3}"; String number = "( |-)[0-9]{4}$"; phoneMask = Pattern.compile(countryCode + areaCode + prefix + number); } public Object getAsObject(FacesContext context, UIComponent component, String value) { System.out.println("PhoneConverter.getAsObject()"); if (value.isEmpty()) { return null; } /* Before we parse, let's see if it really is a phone number. */ if (!phoneMask.matcher(value).matches()) { FacesMessage message = new FacesMessage(); message.setDetail("Phone number not valid"); message.setSummary("Phone number not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ConverterException(message); } /* Now let's parse the string and populate a phone number object. */ PhoneNumber phone = new PhoneNumber(); phone.setOriginal(value); String[] phoneComps = value.split("[ ,()-]"); String countryCode = phoneComps[0]; phone.setCountryCode(countryCode); if ("1".equals(countryCode) && phoneComps.length == 4) { phone.setAreaCode(phoneComps[1]); phone.setPrefix(phoneComps[2]); phone.setNumber(phoneComps[3]); } else if ("1".equals(countryCode) && phoneComps.length != 4) { throw new ConverterException(new FacesMessage( "No Soup for you butter fingers!")); } else if (phoneComps.length == 1 && value.length() > 10){ phone.setCountryCode(value.substring(0,1)); phone.setAreaCode(value.substring(1,4)); phone.setPrefix(value.substring(4,7)); phone.setNumber(value.substring(7)); } else { phone.setNumber(value); } return phone; } public String getAsString(FacesContext context, UIComponent component, Object value) { System.out.println("PhoneConverter.getAsString()"); return value.toString(); } }
Điều tốt đẹp về các trình biến đổi, không giống như các trình duyệt
tính hợp lệ, là bạn có thể đăng ký chúng trong faces-config.xml (xem
[Listing 27](#listing27)) sao cho trình biến đổi được
gắn với một lớp riêng. Bất kỳ thời điểm nào mà lớp xuất hiện trong một
liên kết giá trị của ngôn ngữ biểu thức (EL), trình biến đổi được sử
dụng tự động, bạn không cần phải thêm một thẻ
<f:converter> cho JSP. Trình biến đổi số điện thoại
mới sẽ tự động được áp dụng cho các
PhoneNumber, và bạn không cần phải chỉ rõ trình biến đổi
trong khung nhìn.
Bởi vì việc duyệt tính hợp lệ của số điện thoại cũ là một phần trong việc biến đổi số điện thoại, bạn có thể hỏi trình duyệt tính hợp lệ của một số điện thoại bây giờ sẽ như thế nào. Câu hỏi này có thể được trả lời bằng cách viết một trình duyệt tính hợp lệ, như hiển thị trong Listing 41, để chứng tỏ rằng một số điện thoại là từ Arizona:
##### Listing 41. Hãy bảo đảm số điện thoại là ở AZ
package com.arcmind.contact.validators; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.ValidatorException; import com.arcmind.contact.model.PhoneNumber; public class ContactValidators { public void validatePhone(FacesContext context, UIComponent component, Object value) throws ValidatorException { System.out.println("ContactValidators.validatePhone()"); PhoneNumber phoneNumber = (PhoneNumber)value; if (!phoneNumber.getAreaCode().equals("520") && !phoneNumber.getAreaCode().equals("602")) { FacesMessage message = new FacesMessage(); message.setDetail("Arizona residents only"); message.setSummary("Arizona residents only"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } } }
Lưu ý rằng trình duyệt tính hợp lệ của số điện thoại này, không giống
như cái trước đó, không làm việc với các
Chuỗi ký
tự (String). Khi nó bắt đầu được gọi, trình biến đổi đã
được gọi rồi. Vì thế, giá trị không phải là một
String mà là một
PhoneNumber.
## Làm việc với các trình nghe pha (phase listener)
Trình nghe pha, theo tài liệu hướng dẫn API
của JSF (xem
[Tài nguyên](#resources)), là "một giao
diện được thực hiện bởi các đối tượng muốn được thông báo tại thời
điểm bắt đầu và kết thúc của quá trình xử lý của mỗi pha tiêu chuẩn
trong chu kỳ xử lý yêu cầu" (Sun Microsystems Inc., 2006). Bây giờ khi
bạn đã viết một số trình biến đổi, trình duyệt tính hợp lệ và phương
thức hành động, bạn sẽ viết một số trình nghe pha trong phần này.
Trước JSF 1.2, các
PhaseListener đã được
định nghĩa toàn cục. Trong JSF 1.2, các sự kiện
PhaseListener có thể được đăng ký ở mức khung nhìn hoặc sử
dụng thẻ
<f:phaseListener binding="..."
/>.
### Thực hiện các trình nghe pha
Để thực hiện một
PhaseListener, bạn thực
hiện giao diện
PhaseListener, như hiển thị
trong Listing 42:
##### Listing 42.
DebugPhaseListener
package com.arcmind.phase; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; @SuppressWarnings("serial") public class DebugPhaseListener implements PhaseListener { public void beforePhase(PhaseEvent phaseEvent) { System.out.println("------ BEFORE PHASE " + phaseEvent.getPhaseId()); } public void afterPhase(PhaseEvent phaseEvent) { System.out.println("------ AFTER PHASE " + phaseEvent.getPhaseId()); if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE) { System.out.println("REQUEST END\n\n"); } } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
PhaseListener thông báo cho bạn biết trước
và sau mỗi pha JSF. Bạn báo cho JSF biết bạn quan tâm đến pha nào bằng
phương thức
getPhaseId.
PhaseId.ANY_PHASE ngụ ý rằng bạn muốn được thông báo trước
và sau mỗi pha.
DebugPhaseListener in ra
tên sự kiện pha, do đó bạn có thể thấy sự việc xảy ra ở đâu. Tôi đã
thêm lệnh
System.out.println vào mọi phương
thức duyệt tính hợp lệ, biến đổi, hành động. Tôi cũng đã thêm một lệnh
System.out.println vào các phương thức
get và set của thuộc tính
firstName để bạn
có thể thấy nó khi nó được truy cập.
Tiếp theo, bạn phải đăng ký trình nghe pha trong faces-config.xml, như hiển thị trong Listing 43:
##### Listing 43. Đăng ký
DebugPhaseListener trong faces-config.xml
<lifecycle> <phase-listener>com.arcmind.phase.DebugPhaseListener</phase-listener> </lifecycle>
### Đầu ra của trình nghe pha
Listing 44 cho thấy điều gì sẽ xảy ra khi bạn nạp một biểu mẫu lần đầu tiên:
##### Listing 44. Đầu ra của
DebugPhaseListener khi nạp biểu mẫu lần đầu tiên
------ BEFORE PHASE RESTORE_VIEW 1 ------ AFTER PHASE RESTORE_VIEW 1 ------ BEFORE PHASE RENDER_RESPONSE 6 ContactController.getContacts() ------ AFTER PHASE RENDER_RESPONSE 6 REQUEST END
Dựa vào mã trong
[Listing 42](#listing42), JSF thấy rằng đây
là một yêu cầu khởi đầu cho một khung nhìn, nó sử dụng JSP để xây dựng
khung nhìn, và sau đó đi ngay lập tức sang pha hoàn trả đáp ứng. Lưu ý
rằng phương thức
getContacts() của trình
điều khiển được gọi trong pha hoàn trả đáp ứng.
Khi bạn nhấn vào liên kết
**Add New**, bạn thấy kết quả được hiển
thị trong Listing 45:
##### Listing 45. Đầu ra của
DebugPhaseListener sau khi phương thức
addNew() được gọi
------ BEFORE PHASE RESTORE_VIEW 1 ------ AFTER PHASE RESTORE_VIEW 1 ------ BEFORE PHASE APPLY_REQUEST_VALUES 2 ------ AFTER PHASE APPLY_REQUEST_VALUES 2 ------ BEFORE PHASE PROCESS_VALIDATIONS 3 ------ AFTER PHASE PROCESS_VALIDATIONS 3 ------ BEFORE PHASE UPDATE_MODEL_VALUES 4 ------ AFTER PHASE UPDATE_MODEL_VALUES 4 ------ BEFORE PHASE INVOKE_APPLICATION 5 ContactController.addNew() ------ AFTER PHASE INVOKE_APPLICATION 5 ------ BEFORE PHASE RENDER_RESPONSE 6 Contact.getFirstName() ContactController.getGroups() ContactController.getGroups() ContactController.getAvailableTags() ContactController.getContacts() ------ AFTER PHASE RENDER_RESPONSE 6 REQUEST END
Phương thức
addNew() gây ra sự hoàn trả biểu
mẫu. Bởi vì
addNew() là một khung nhìn hiện
lại (postback), JSF trải qua tất cả các pha. Bởi vì biểu mẫu đã không
được hoàn trả trước khi phương thức
addNew() được gọi, các trường của nó không được xử lý
trước khi phương thức
addNew() được gọi.
Tiếp theo, nhập một mã ZIP sai (vi dụ như
*aaa*) và một số điện
thoại sai (ví dụ như *aaa*). Sau đó chọn hai thẻ và một nhóm, và
nhấn nút **Add** . Bạn sẽ thấy các kết quả hiển thị trong Listing
46:
##### Listing 46. Kết quả sau khi nhập vào một mã zip sai, số điện thoại sai, một nhóm, và một số thẻ
------ BEFORE PHASE RESTORE_VIEW 1 ------ AFTER PHASE RESTORE_VIEW 1 ------ BEFORE PHASE APPLY_REQUEST_VALUES 2 ------ AFTER PHASE APPLY_REQUEST_VALUES 2 ------ BEFORE PHASE PROCESS_VALIDATIONS 3 GroupConverter.getAsObject ContactController.getAvailableGroups() ZipCodeValidator.validate() ContactValidators.validatePhone() TagConverter.getAsObject TagConverter.getAsObject ContactController.getAvailableTags() ContactController.getAvailableTags() ------ AFTER PHASE PROCESS_VALIDATIONS 3 ------ BEFORE PHASE RENDER_RESPONSE 6 ContactController.getAvailableGroups() GroupConverter.getAsString GroupConverter.getAsString GroupConverter.getAsString ContactController.getAvailableTags() TagConverter.getAsString TagConverter.getAsString TagConverter.getAsString TagConverter.getAsString ContactController.getContacts() ------ AFTER PHASE RENDER_RESPONSE 6 REQUEST END
Trong pha xử lý duyệt tính hợp lệ, phương thức
GroupConverter.getAsObject() được gọi cho nhóm đã được
chọn.
TagConverter.getAsObject()được gọi
hai lần, một lần cho mỗi thẻ đã được chọn. Cả hai trình duyệt tính hợp
lệ tuỳ biến được bạn viết —
ZipCodeValidator.validate() và
ContactValidators.validatePhone() — được gọi trong pha xử lý duyệt tính hợp lệ. Trong pha
hoàn trả đáp ứng phương thức
getAsString()
của các trình biến đổi được gọi cho mỗi đối tượng trong danh sách các
nhóm đang có và các thẻ đang có.
Bây giờ bạn có thể tưởng tượng nếu
ContactController.getAvailableGroups() và
ContactController.getAvailableTags() truy cập
vào cơ sở dữ liệu mỗi lần thì bạn sẽ truy cập vào cơ sở dữ liệu bốn
lần để có được các danh sách này. Bạn sẽ muốn thêm một số logic để bạn
sẽ truy cập cơ sở dữ liệu chỉ một lần cho mỗi yêu cầu. Bạn có thể, ví
dụ, nạp dữ liệu từ hành động chỉ khi nó chưa được nạp.
Một cuộc thảo luận về các pha JSF sẽ chưa hoàn tất nếu không cho thấy kết quả của việc đệ trình một biểu mẫu hợp lệ, như hiển thị trong Listing 47:
##### Listing 47. Tất cả các con đường đã thông
------ BEFORE PHASE RESTORE_VIEW 1 ------ AFTER PHASE RESTORE_VIEW 1 ------ BEFORE PHASE APPLY_REQUEST_VALUES 2 ------ AFTER PHASE APPLY_REQUEST_VALUES 2 ------ BEFORE PHASE PROCESS_VALIDATIONS 3 GroupConverter.getAsObject ContactController.getAvailableGroups() ZipCodeValidator.validate() ContactValidators.validatePhone() ContactValidators.validatePhone() ContactValidators.validatePhone() TagConverter.getAsObject TagConverter.getAsObject ContactController.getAvailableTags() ContactController.getAvailableTags() ------ AFTER PHASE PROCESS_VALIDATIONS 3 ------ BEFORE PHASE UPDATE_MODEL_VALUES 4 Contact.setFirstName() ------ AFTER PHASE UPDATE_MODEL_VALUES 4 ------ BEFORE PHASE INVOKE_APPLICATION 5 ContactController.persist() ------ AFTER PHASE INVOKE_APPLICATION 5 ------ BEFORE PHASE RENDER_RESPONSE 6 ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() Contact.getFirstName() ContactController.getContacts() ContactController.getContacts() ------ AFTER PHASE RENDER_RESPONSE 6 REQUEST END
Listing 47 cho thấy những gì đang diễn ra qua tất cả các pha của vòng
đời một yêu cầu. Lưu ý rằng bạn bước vào pha
INVOKE_APPLICATION và phương thức
persist() được gọi:
ContactController.persist().
### Thêm một khung công tác duyệt tính hợp lệ ở mức đối tượng
Bạn có thể sử dụng một
PhaseListener để bắt
giữ các sự kiện nghe pha và thay đổi cách JSF xử lý các yêu cầu. Giả
sử bạn muốn thêm vào JSF một khung công tác duyệt tính hợp lệ ở
mức-đối tượng riêng của bạn. Bạn có thể sử dụng một giao diện giống
như giao diện được hiển thị trong Listing 48:
##### Listing 48. Giao diện trình duyệt tính hợp lệ (Validator)
package com.arcmind.contact.model; public interface Validateable { public void validate() throws ValidationException; }
Sau đó, bạn thay đổi đối tượng
Contact của
mình để thực hiện giao diện này, như hiển thị trong Listing 49:
##### Listing 49. Thực hiện giao diện trình duyệt tính hợp lệ của
Contact
package com.arcmind.contact.model; import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.List; @SuppressWarnings("serial") public class Contact implements Serializable, Validateable { ... public void validate() throws ValidationException { if ( homePhoneNumber == null && workPhoneNumber == null && mobilePhoneNumber == null ) { throw new ValidationException("At least one phone number " + "must be set", ""); } } ...
Bạn có thể tạo ra một
PhaseListener để tìm
kiếm các đối tượng
có khả năng duyệt tính hợp lệ
(Validateable) và duyệt tính hợp lệ của chúng.
PhaseListener có thể nhận các thông báo ngoại
lệ và biến đổi chúng thành các
FacesMessage, nhưng làm thế nào để bạn biết khi nào đối tượng
cần phải được duyệt tính hợp lệ? Có vẻ như là kiến thức đó sẽ chỉ có
trong lớp của trình điều khiển. Vì vậy, để xử lý điều này theo một
cách tổng quát, bạn có thể tạo ra một trình điều khiển lớp mẹ
(superclass) để tạo một lớp con
PhaseListener không tên bên trong mà bạn có thể liên kết
tới nó, như hiển thị trong Listing 50:
##### Listing 50. Lớp cơ sở với
PhaseListener
package com.arcmind.contact.controller; import java.io.Serializable; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import com.arcmind.contact.model.Validateable; import com.arcmind.contact.model.ValidationException; @SuppressWarnings("serial") public abstract class AbstractCrudController implements Serializable { private boolean edit; public PhaseListener phaseListener = new PhaseListener() { public void afterPhase(PhaseEvent event) { validate(); } public void beforePhase(PhaseEvent event) { } public PhaseId getPhaseId() { return PhaseId.UPDATE_MODEL_VALUES; } }; abstract Object getFormObject(); //subclass defines this private void validate() { Object form = getFormObject(); if (! (form instanceof Validateable) || form == null) { return; } Validateable validateable = (Validateable) form; try { validateable.validate(); //validate object } catch (ValidationException validationException) { FacesContext.getCurrentInstance().renderResponse(); //Do not invoke application. addErrorMessage(validationException.getMessage()); } } public PhaseListener getPhaseListener() { return phaseListener; } protected void addErrorMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( FacesMessage.SEVERITY_ERROR, message, null)); } public boolean isEdit() { return edit; } public void setEdit(boolean edit) { this.edit = edit; } }
Lưu ý rằng
PhaseListener chỉ lắng nghe
UPDATE_MODEL_VALUES. Sau đó nó gọi phương
thức
validate() để kiểm tra xem đối tượng
biểu mẫu có mặt không và có là một cá
thể của
Validator trước khi nó gọi
validate() trên đối tượng miền. Sau đó nó gọi
validate() và cho phép mô hình thực
hiện việc duyệt tính hợp lệ. Cũng lưu ý rằng, nếu việc duyệt tính hợp
lệ của mô hình thất bại, phương thức
validate() của
AbstractCrudCOntroller gọi
FacesContext.getCurrentInstance().renderResponse(), để
buộc JSF bỏ qua pha
INVOKE_APPLICATION.
Bây giờ bạn thay đổi
ContactController thành
lớp con của
AbstractCrudController, như
hiển thị trong Listing 51:
##### Listing 51.
ContactController mở rộng
AbstractCrudController để cung cấp hỗ trợ duyệt tính hợp lệ của
PhaseListener
public class ContactController extends AbstractCrudController{ ... public void addNew() { ... same as before except sets the edit mode. super.setEdit(true); } public void persist() { ... same as before except sets the edit mode. super.setEdit(false); } public void read() { ... same as before except sets the edit mode. super.setEdit(true); } @Override Object getFormObject() { if (super.isEdit()) { return this.contact; } else { return null; } } }
Lưu ý rằng Listing 51 thực hiện phương thức trừu tượng
getFormObject của lớp
AbstractCrudController. Nó sử dụng chế độ chỉnh sửa sao
cho nó trả về đối tượng mối giao tiếp chỉ khi trình điều khiển ở trong
chế độ chỉnh sửa. Bằng cách này, trình điều khiển quyết định khi nào
đối tượng cần được duyệt tính hợp lệ.
Cuối cùng, bạn cần phải đăng ký trình nghe pha của bạn, như hiển thị trong Listing 52:
##### Listing 52. . Liên kết vào thuộc tính
phaseListener
<f:view>
Contacts (4th version)
 <f:phaseListener binding="#{contactController.phaseListener}" />
Với trình nghe pha, bạn có thể thay đổi cách JSF xử lý yêu cầu, do đó mở rộng khung công tác của JSF cho phù hợp với các nhu cầu của bạn.
## Kết luận
Hướng dẫn này đã trình bày về vòng đời của quá trình xử lý yêu cầu của
JSF và chứng tỏ một số các đặc tính cốt yếu của mô hình thành phần
JSF. Nó đã trình bày khá nhiều về biến đổi và duyệt tính hợp lệ của
JSF. Thực vậy, nó đã bao gồm hầu hết những gì bạn cần biết về các quá
trình xử lý này để làm cho chúng hoạt động trong các ứng dụng của bạn
(ít nhất là với phiên bản này của JSF)! Tất nhiên, hướng dẫn này không
thể bao gồm tất cả mọi thứ. Ví dụ, bạn có thể muốn xem qua Tomahawk,
Crank, Shale, và jsf-comp để biết về các thành phần duyệt tính hợp lệ
không được cung cấp trong JSF hoặc được thảo luận ở đó (xem
[Tài nguyên](#resources)). Và mặc dù hướng dẫn này đã
thảo luận các kỹ thuật biến đổi và duyệt tính hợp lệ phổ biến nhất,
vẫn còn có các kỹ thuật khác không được đưa ra trong hướng dẫn này.
Một điều cần ghi nhớ là việc biến đổi và duyệt tính hợp lệ không nhất
thiết làm việc tốt với nhau. Phép biến đổi sẽ biến đổi các chuỗi ký tự
thành các đối tượng, trong khi hầu hết các trình duyệt tính hợp lệ
tiêu chuẩn làm việc trên các chuỗi ký tự. Vì vậy, bạn cần phải thận
trọng khi sử dụng các trình biến đổi và các trình duyệt tính hợp lệ
tùy biến cùng với nhau. Ví dụ, đối tượng
PhoneNumber của hướng dẫn này sẽ không làm việc với một
trình duyệt tính hợp lệ theo chiều dài. Trong trường hợp này, hoặc bạn
sẽ phải viết một trình duyệt tính hợp lệ theo yêu cầu, hoặc đơn giản
là bao gồm bất kỳ logic duyệt tính hợp lệ đặc biệt nào vào trong trình
biến đổi tùy biến. Tôi thích lựa chọn sau bởi vì nó cho phép bạn chỉ
đơn giản kết hợp một trình biến đổi tùy biến (có logic duyệt tính hợp
lệ làm sẵn) với một kiểu đối tượng cụ thể và yêu cầu JSF xử lý kiểu
đối tượng đó. JSF thực hiện điều này cho bạn một cách tự động mà không
bắt bạn chỉ rõ các mã nhận dạng
id của
trình biến đổi nào trong JSP. (Tất nhiên, một số người có thể gọi đây
là lập trình lười nhác, và nó không nhất thiết là giải pháp tốt nhất
cho tất cả các trường hợp sử dụng).
JSF cung cấp một khung công tác linh hoạt, mạnh mẽ, và có khả năng cắm được cho việc phát triển ứng dụng web. Ngoài các trình biến đổi và trình duyệt tính hợp lệ tiêu chuẩn, JSF tạo thuận tiện cho việc triển khai thực hiện tùy biến phù hợp như nhau với cả nhà phát triển ứng dụng và cả nhà phát triển khung công tác. Cuối cùng, chiến lược biến đổi và duyệt tính hợp lệ mà bạn chọn là tùy thuộc vào bạn. JSF cho phép bạn dễ dàng và nhanh chóng bắt đầu (các trình biến đổi, các trình duyệt tính hợp lệ tiêu chuẩn, duyệt tính hợp lệ nội tuyến) trong khi làm bản mẫu đầu tiên, và dịch chuyển đến các giải pháp sản xuất tinh vi hơn (các đối tượng tùy biến, các thông báo tùy biến) trong các pha phát triển sau này. Và xuyên suốt tất cả, vòng đời của ứng dụng JSF cung cấp một cơ sở hạ tầng tin cậy cho việc bảo đảm trước sau như một tính toàn vẹn của dữ liệu-mô hình.
## Tải về
|Mô tả||Tên||Kích thước|
|Mã ví dụ của bài học này|
## Tài nguyên
### Học tập
- "
[Getting started with Java Server Faces 1.2, Part 1: Building basic applications](http://www.ibm.com/developerworks/vn/edu/j-jsf1/index.html)" (developerWorks, 12.2007): Một cái nhìn toàn cảnh về các đặc tính của JSF và học cách làm thế nào để viết một ứng dụng JSF cơ bản. Xây dựng và chỉnh sửa một ứng dụng máy tính bỏ túi đơn giản. [Source code instructions:](http://www.thearcmind.com/confluence/display/JSFTut/JSF+Tutorial+2)Tìm chỉ dẫn về cách làm thế nào để chạy mã nguồn của hướng dẫn này với Eclipse JEE và với Tomcat và Maven 2. [JSF Tag Library Documentation](http://java.sun.com/javaee/javaserverfaces/1.2_MR1/docs/tlddocs/index.html): Tài liệu chính thức về các thư viện thẻ
htmlvà
lõi.
[JSF API](http://java.sun.com/javaee/javaserverfaces/1.2_MR1/docs/api/index.html):Javadocs API về đặc tả kỹ thuật của JSF.
- "
[Web Tier to Go With Java EE 5: Summary of New Features in JavaServer Faces 1.2 Technology](http://java.sun.com/developer/technicalArticles/J2EE/jsf_12/)": (Jennifer Ball và Ed Burns, java.sun.com, 02.2006): Đọc về các tính năng mới trong JSF 1.2.
- "
[Web Tier to Go With Java EE 5: Summary of New Features in JSP 2.1 Technology](http://java.sun.com/developer/technicalArticles/J2EE/jsp_21/)" (Pierre Delisle và Jennifer Ball, java.sun.com, 02.2006): Một cái nhìn toàn cảnh về các tính năng mới được giới thiệu trong phiên bản 2.1 của công nghệ JSP. [: Tìm một danh sách các mẫu định dạng ngày tháng ở đây.](http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html)
java.text.SimpleDateFormat
[Crank](http://code.google.com/p/krank/): Crank là khung công tác duyệt tính hợp lệ hướng chú thích, CRUD, chính/chi tiết được xây dựng với JPA, JSF, các Facelet, và Ajax. Crank là một bản phân tích ca sử dụng của những gì có thể với chồng JSF 2.0 mới. [JSF-comp](http://jsf-comp.sourceforge.net/components/clientvalidators/index.html): Một thư viện thành phần để duyệt tính hợp lệ phía khách. [Apache Shale](http://shale.apache.org/): Một khung công tác của ứng dụng Web dựa trên JSF. [Apache MyFaces Tomahawk](http://myfaces.apache.org/tomahawk/): Các thành phần tương thích JSF. [Facelets](https://facelets.dev.java.net/): Facelets là một công nghệ khung nhìn, tập trung vào việc xây dựng các cây thành phần JSF.
- "
[Facelets fits JSF like a glove](http://www.ibm.com/developerworks/java/library/j-facelets/?S_TACT=105AGY75)" (developerWorks, 02.2006): Trong bài viết này, tác giả giới thiệu các thành phần cấu tạo có thể dùng lại và để làm khuôn mẫu kiểu HTML dễ dàng của Facelets.
- "
[Advanced Facelets programming](http://www.ibm.com/developerworks/java/library/j-facelets2.html?S_TACT=105AGY75)" (Richard Hightower, developerWorks, 05.2006): Đọc về các cách tiên tiến để nối cầu khoảng hở giữa JSF và EL.
- Duyệt qua
[kho sách công nghệ](http://www.ibm.com/developerworks/apps/SendTo?bookstore=safari&S_TACT=105AGY75)để tìm các sách về chủ đề kỹ thuật này và các chủ đề kỹ thuật khác. [developerWorks Java technology zone](http://www.ibm.com/developerworks/java?S_TACT=105AGY75): Hàng trăm bài viết về mọi khía cạnh lập trình Java.
### Lấy sản phẩm và công nghệ
- Xây dựng dự án
phát triển tiếp theo của bạn với
[các phần mềm thử nghiệm của IBM](http://www.ibm.com/developerworks/downloads/?S_TACT=105AGY75), IBM có sẵn để tải về trực tiếp từ developerWorks.
### Thảo luận
- Xem
[developerWorks blogs](http://www.ibm.com/developerworks/blogs/?S_TACT=105AGY75)và tham gia cộng đồng [developerWorks](http://www.ibm.com/developerworks/community?S_TACT=105AGY75).
## Bình luận
[Vùng dữ liệu trên developerWorks](http://www.ibm.com/developerworks/vn/views/data/libraryview.jsp)
Khám phá các bài viết xoay quanh lĩnh vực dữ liệu, Quản lý hệ thống thông tin
[Vùng mã nguồn mở trên developerWorks](http://www.ibm.com/developerworks/vn/views/opensource/libraryview.jsp)
Xem các bài viết về HTML, CSS, JavaScript,...
[Điện toán đám mây](http://www.ibm.com/developerworks/vn/views/cloud/libraryview.jsp)
Tìm hiểu về điện toán đám mây

Content-Language: en-US


markdown:
UKR-SIG Search
To visualize the dropdown menu you have to activate Javascript.
Goto the SITE MAP
Home
Get Started
Get started with UKR-SIG
Introduction to UKR-SIG
Ukraine SIG FAQ
UKR-SIG Projects
Get started with this website
How to use our website
Site Map
Most Visited
Ukraine Clickable Map
Alphabetical List of Towns
Master Index of Documents
Master Index of Projects
Master Index of People
Towns & Districts
Ukraine Clickable Map
Alphabetical List of Towns
Lost Towns
Maps Gallery
Research
UKR-SIG Databases
Master Index of Projects
Master Index of Documents
Master Index of Names (MNI)
JewishGen Ukraine Database (JGUD)
Ukraine Given Name Database (GNDB)
Links to Research Resources
Research Reference Tools
SIG's Discussion List Archive
InfoSheets
Photos, Travel & More
Photo Gallery
Famous Jews from Ukraine
Faces from Chernigoff
Faces from Kherson
Faces from Kiev
Faces from Podolia
Faces from Poltava
Faces from Volhynia
Faces from Yekaterinoslav
Travel Reports on UkrSIG
Links to Travel Reports
Stories
People
Recipes
Book Reviews
Historical Documents
Other Articles
About Us
UKR-SIG Board of Directors
UKR-SIG Key People
UKR-SIG Surveys
UKR-SIG News Archive
UKR-SIG Newsletter
Subscribe to UKR-SIG Mailing List
UKR-SIG on Facebook
Conferences
Donate / Contribute
Donate
Contribute
Town Leaders Needed
Acknowledgements
Collection: Revision List, Aleksandrovka town, Pereyaslav District 1835, FHL Catalog entry
Collection Name:
Revision List, Aleksandrovka town, Pereyaslav District 1835, FHL Catalog entry
Geographic Area:
Aleksandrovka (Town)
Type:
Census: Revision Lists
Time frame:
1835
Description:
Entries on this microfilm for Aleksandrovka do NOT include Jews
. 1 microfilm reel at FHL, microfilm reel 2396624, part 2. This part of the film contains entries for towns of Aleksandrovka and Rymerovka. The entries on this film are for nobility and peasants.
Location:
FHL
Availability:
SELECT from List
Related Projects
No current projects available
Collection Names in Ukraine SIG Master Name Index
Names have not yet been entered
Last Modified: 03-16-2013
Site Map
Site Index
Contact Us
Donate
JewishGen Home
We need your help !!!
Please complete Ukraine SIG's Surveys
Tweet
Copyright � 2012, Ukraine SIG. All rights reserved.

WARC-Type: response

Content-Type: application/http; msgtype=response

Cache-Control: private

Content-Type: text/html

Connection: close


markdown:
Thiết kế web bán hàng
Đăng ký tên miền
Doanh nhân
Tên miền miễn phí
Email Marketing
Phần mềm bán hàng
y tuong kinh doanh
kinh nghiệm bán hàng
nuoi bo cau
Cách tết tóc đẹp
Cách làm trắng da
Chăm sóc da mặt
Tổ chức đám cưới
Cách tán gái
Cách trang điểm nhẹ
Giảm cân hiệu quả
Xem tử vi
Handmade
Váy công sở
Chuyện hẹn hò
Sinh con trai
Mon ngon moi ngay
cách nấu bò kho
cách búi tóc đẹp
cách hôn
suon xao chua ngot
toc xoan
cách làm giá đỗ
sinh con trai
cắm hoa nghệ thuật
that bim toc
làm đồ handmade
Cúng rằm tháng 7
lễ vu lan
áo crop top
cách nấu bún bò huế
lau thai
cach lam giau
nhung kieu toc ngan dep
cách cắm hoa đẹp
cho chihuahua
Địa chỉ: 442 Đội Cấn, Phường Cống Vị, Quận Ba Đình, Hà Nội - ĐT: 04 66558868 - Fax: 04 37868904
Giấy phép số 1669/GP-STTTT do Sở Thông tin và Truyền thông Hà Nội cấp ngày 12/06/2014.

Content-Type: text/html; charset=utf-8

X-Powered-By: UrlRewriter.NET 2.0.0


markdown:
Tên truy cập
Mật khẩu
Đăng ký thành viên
Quên mật khẩu
Gửi thư kích hoạt
Copyright © 2007 - 2015 NhaBan.com - All rights Reserved < + Nhaban.com - Chuyên trang mua ban nha dat được xây dựng bởi CÔNG TY TNHH SIÊU TRỰC TUYẾN + Giấy đăng ký kinh doanh: 4102056839/ Sở kế hoạch đầu tư TP.HCM, cấp ngày 15/12/2007 + Giấy phép số: 05/GP-TTĐT do Cục QL Phát Thanh, Truyền Hình Và Thông Tin Điện Tử - Bộ Thông Tin và Truyền Thông cấp + Địa chỉ : 139 - Đường số 13B - Tiểu Khu II - KDC An Lạc - P. Bình Trị Đông B - Q. Bình Tân - TP. Hồ Chí Minh + Điện thoại liên lạc : (08) 38777939. - Fax : (08) 62602665. + Thời gian làm việc: Thứ 2 - 6 (8h - 17h), Thứ 7 (8h - 12h)

WARC-Truncated: length

Cache-Control: private, must-revalidate

Pragma: no-cache

Vary: Accept-Encoding,User-Agent

Content-Encoding: gzip

Content-Type: text/html; charset=UTF-8


markdown:
**Cloud Của Tui có gì hot?**
- Thêm Bài hát/Playlist yêu thích vào Cloud.
- Tạo Playlist riêng theo sở thích âm nhạc.
- Tự động đồng bộ nếu sử dụng một tài khoản Cloud cho nhiều thiết bị.
- Nghe nhạc Offline dễ dàng mà không sợ phải tìm kiếm Playlist để tải lại nếu thay đổi thiết bị.

Set-Cookie: NCTNPLS=08998af003f7d4b28475c79d01e43f89;Path=/;Domain=.nhaccuatui.com;Expires=Mon, 02-Mar-2015 05:30:14 GMT;HttpOnly

P3P: CP='NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM'

Server: nginx+