Xử lý các sự kiện và cập nhật giao diện người dùng
Nhiều thành phần JSF HTML có các thuộc tính liên quan đến JavaScript cho phép bạn chỉ định các đoạn mã, được thực hiện trong trình duyệt Web khi một sự kiện UI (giao diện người dùng) nào đó xảy ra. Ví dụ, có bảy loại sự kiện về chuột được các thành phần JSF chuẩn hỗ trợ:
-
onmouseover -
onmouseout -
onmousemove -
onmousedown -
onmouseup -
onclick -
ondblclick
Khi một thành phần UI tập trung hay không tập trung vào bàn phím, nó
tạo các sự kiện có thể bắt giữ được thông qua các thuộc tính onfocus và onblur. Các
sự kiện onkeydown, onkeyup và onkeypress được thực
hiện khi một phím được nhấn hoặc nhả ra. Ngoài ra, thành phần <h:form> chấp nhận các thuộc tính onsubmit và onreset và
các thành phần đầu vào có các thuộc tính onchange và onselect, có thể được
sử dụng để gọi một hàm JavaScript khi trạng thái của phần tử biểu mẫu thay
đổi.
Bạn cũng có thể sử dụng các thuộc tính có liên quan đến
JavaScript của các phần tử HTML trực tiếp có trong một trang JSF thay vì
đang được hoàn trả bởi các thành phần JSF. Ví dụ, thẻ <body> có các thuộc tính onload và onunload. Sự kiện onload được thực hiện khi hoàn thành việc nạp một
trang trong trình duyệt Web. Sự kiện onunload
xảy ra khi người dùng rời khỏi trang này.
Một trình xử lý sự kiện
JavaScript điển hình sử dụng các DOM API trong trình duyệt Web để cập nhật
các đặc tính của các phần tử HTML được các thành phần JSF hoàn trả. Bạn có
thể dễ dàng xác định vị trí các đối tượng biểu diễn các phần tử HTML khi
sử dụng DOM Core API. Ví dụ, bạn có thể sử dụng document.getElementById(...) để tìm một phần tử có mã nhận
dạng ID đã biết.
DOM HTML API mở rộng DOM Core API, bổ sung thêm các
phương thức và các đặc tính cụ thể cho các văn bản HTML. Sử dụng document.forms.myFormId để nhận được các đối
tượng biểu diễn một biểu mẫu trong trình duyệt Web và sau đó lấy được một
mảng của các đối tượng biểu diễn các phần tử của biểu mẫu bằng myForm.elements. Một đặc tính rất có ích là className, cho phép bạn thay đổi thuộc tính class (lớp) của một phần tử HTML.
Đặc tả
DOM HTML (xem Tài nguyên) mô tả tất cả các đặc
tính và các phương thức tiêu chuẩn của các đối tượng biểu diễn các phần tử
của một trang ở phía máy khách. Hầu hết các trình duyệt Web, bao gồm IE,
Firefox, Netscape, Safari và Opera, hỗ trợ các đặc tính bổ sung, chẳng hạn
như innerHTML, cho phép bạn thay đổi các nội
dung của một phần tử HTML.
Các ví dụ trong phần này hiển thị cách sử dụng các thuộc tính có liên quan đến JavaScript của các thành phần JSF HTML và cách cập nhật các giao diện người dùng bằng cách sử dụng HTML DOM API.
Việc đặt các kịch bản lệnh trong các trang JSF
Mã JavaScript có
thể được chèn vào trong một trang JSF như trong bất kỳ trang Web thông
thường nào, sử dụng các phần tử <script>
của HTML (xem Liệt kê 1). Bạn có thể sử dụng mã JavaScript để tạo nội dung
HTML với document.write() trong trình duyệt
Web, nhưng điều này hiếm khi cần. Trong hầu hết trường hợp, bạn sẽ đặt các
phần tử <script> trong tiêu đề của trang,
trong đó sẽ chứa các hàm JavaScript được gọi từ các thuộc tính sự kiện,
chẳng hạn như onclick, onsubmit và onchange. Bạn cũng có
thể sử dụng phần tử <noscript> để cảnh
báo người dùng nếu JavaScript bị vô hiệu hóa trong trình duyệt của
họ.
Liệt kê 1. Sử dụng thẻ <script>
<html>
<head>
<script type="text/javascript"> function myEventHandler(...)
{ ... } </script>
</head>
<body>
<noscript> This page requires JavaScript. </noscript> ...
</body>
</html> |
Đặt mã JavaScript của bạn
trong tệp .js nếu bạn muốn gọi các hàm giống
nhau trong nhiều trang. Các kịch bản lệnh ngoài phải được nhập khẩu vào
các trang Web, sử dụng thuộc tính src của thẻ
<script> (xem Liệt kê 2). Trong
trường hợp này, chắc chắn là các tiền tố /faces/ không được thêm vào URL của kịch bản lệnh này, URL này
có thể xảy ra nếu bạn sử dụng một URI tương đối trong thuộc tính src. Cách đơn giản nhất để tránh những vấn đề này
là sử dụng hậu tố .faces. Nếu bạn thích tiền tố
/faces/ để yêu cầu các trang JSF, hãy xác
định một URI tuyệt đối cho tệp JavaScript, bao gồm đường dẫn ngữ cảnh
trong thuộc tính src của thẻ <script>.
Liệt kê 2. Nhập khẩu các kịch bản lệnh ngoài
<script
type="text/javascript" src="${pageContext.request.contextPath}/scripts/MyScript.js">
</script>
<script type="text/javascript"
src="<%=request.getContextPath()%>/AnotherScript.js">
</script> |
Ẩn dấu và hiển thị các thành phần JSF tùy chọn
Trong Phần 1 của loạt bài này, bạn đã thấy cách thiết lập các lớp phong
cách của các thành phần JSF ở phía máy chủ bằng cách sử dụng thuộc tính
styleClass. Bạn cũng có thể thiết lập hoặc
thay đổi các lớp phong cách ở phía máy khách bằng cách sử dụng JavaScript
và DOM. Ví dụ sau chỉ ra cách thu hẹp và mở rộng một nhóm các thành phần
tùy chọn khi sử dụng đặc tính CSS display (hiển
thị). Một biểu mẫu tìm kiếm đơn giản (xem Hình 1) có chứa một trường văn
bản yêu cầu, hai hộp kiểm tra và một danh sách thả xuống. Khi người sử
dụng đánh dấu chọn More Options (Nhiều tùy chọn hơn), ô chứa thành
phần Match Case (Trường hợp giống nhau) và Language (Ngôn ngữ) được hiển
thị. Nếu người dùng không đánh dấu chọn More Options, các thành
phần tùy chọn bị thu hẹp lại.
Hình 1. Ví dụ SearchForm
Ví dụ SearchForm.jsp (xem Liệt kê 3) sử dụng các
thành phần JSF chuẩn để xây dựng Web từ đó. Các thành phần tùy chọn được
đặt trong một thùng chứa <h:panelGrid>
được hoàn trả như là một bảng HTML. Ô tùy chọn được nhìn thấy hoặc ẩn dấu
ở phía máy khách khi sử dụng một hàm JavaScript có tên là updatePanelClass(). Như tên gọi của nó cho thấy,
hàm này thay đổi lớp phong cách của phần tử <table> được <h:panelGrid> hoàn trả. Hàm updatePanelClass() được gọi mỗi khi người dùng thay đổi trạng
thái của hộp kiểm tra có nhãn là More Options vì cuộc gọi updatePanelClass() được mã hoá trong các thuộc
tính onclick của các thành phần <h:selectBooleanCheckbox>.
Liệt kê 3. Ví dụ SearchForm.jsp
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<f:view>
<html>
<head>
<title>Search Form </title> ...
</head>
<body onload="initForm()"> <h1>Search Form</h1>
<h:form id="searchForm">
<h:panelGrid columns="1" border="0" cellspacing="5">
<h:panelGroup>
<h:outputLabel value="Text: " for="text"/>
<h:inputText id="text" value="#{searchBean.text}" required="true"
requiredMessage="Required" size="20"/>
<h:message for="text"/>
</h:panelGroup>
<h:panelGroup>
<h:selectBooleanCheckbox id="moreOptions" value="#{searchBean.moreOptions}"
onclick="updatePanelClass()"/>
<h:outputLabel value="More Options" for="moreOptions"/>
</h:panelGroup>
<h:panelGrid id="optionsPanel" columns="1" border="0" cellspacing="5">
<h:panelGroup>
<h:selectBooleanCheckbox id="matchCase" value="#{searchBean.matchCase}"/>
<h:outputLabel value="Match Case"
for="matchCase"/>
</h:panelGroup>
<h:panelGroup>
<h:outputLabel value="Language: " for="language"/>
<h:selectOneMenu id="language" value="#{searchBean.language}">
<f:selectItem itemValue="English" itemLabel="English"/>
<f:selectItem itemValue="Spanish" itemLabel="Spanish"/>
<f:selectItem itemValue="French" itemLabel="French"/>
</h:selectOneMenu>
</h:panelGroup>
</h:panelGrid>
<h:commandButton id="search" value="Search"
action="#{searchBean.searchAction}"/>
</h:panelGrid>
</h:form>
</body>
</html>
</f:view> |
Trang
SearchForm.jsp tạo ra HTML được hiển thị
trong Liệt kê 4. Quan sát cách các thành phần JSF chuẩn thêm tiền tố searchForm: đến các mã nhận dạng (ID) của các
phần tử HTML được hoàn trả bởi các thành phần lồng nhau trong biểu mẫu JSF
có ID của nó là searchForm.
Liệt kê 4. SearchForm.jsp tạo ra HTML
<html>
<head>
<title>Search Form</title> ...
</head>
<body onload="initForm()">
<h1>Search Form</h1>
<form id="searchForm" name="searchForm" method="post"
action="/jsf12js/SearchForm.faces"
enctype="application/x-www-form-urlencoded"> ...
<table border="0" cellspacing="5">
<tbody> <tr> <td>
<label for="searchForm:text">Text: </label>
<input id="searchForm:text" type="text" name="searchForm:text" size="20" />
</td> </tr> <tr> <td>
<input id="searchForm:moreOptions"
type="checkbox" name="searchForm:moreOptions"
checked="checked" onclick="updatePanelClass()" />
<label for="searchForm:moreOptions">More Options</label></td>
</tr> <tr> <td>
<table id="searchForm:optionsPanel" border="0" cellspacing="5">
<tbody> <tr> <td>
<input id="searchForm:matchCase" type="checkbox" name="searchForm:matchCase" />
<label for="searchForm:matchCase">Match Case</label></td>
</tr> <tr> <td>
<label for="searchForm:language">Language: </label>
<select id="searchForm:language" name="searchForm:language" size="1">
<option value="English">English</option>
<option value="Spanish">Spanish</option>
<option value="French">French</option>
</select></td> </tr>
</tbody> </table> </td>
</tr> <tr> <td><input
id="searchForm:search" type="submit" name="searchForm:search" value="Search"
/></td> </tr> </tbody>
</table> ... </form> </body>
</html> |
Liệt
kê 5 cho thấy phần tử <style> của trang
SearchForm.jsp có tiêu đề của nó chứa cả
các lớp phong cách và cả các hàm JavaScript. Lớp visible (có thể nhìn thấy) chỉ đặt lề trái của ô tùy chọn và
không cần bất kỳ giá trị cài đặt nào khác vì bảng HTML được hiển thị theo
mặc định. Lớp hidden (ẩn) thiết lập đặc tính
CSS display tới none
(không), tắt chức năng hiển thị của bảng
này.
Liệt kê 5. Các lớp phong cách của SearchForm.jsp
<style type="text/css"> .visible {
margin-left: 40px; } .hidden { display: none; } </style> |
Hàm
updatePanelClass() (xem Liệt kê 6) xác định
vị trí hộp kiểm tra searchForm:moreOptions và
bảng searchForm:optionsPanel với document.getElementById(). Các đối tượng panel biểu diễn phần tử <table id="searchForm:optionsPanel"> được <h:panelGrid id="optionsPanel"> hoàn trả và
các đối tượng checkbox (hộp kiểm tra) biểu diễn
phần tử <input
id="searchForm:moreOptions"> được <h:selectBooleanCheckbox id="moreOptions"> hoàn trả. Hàm
updatePanelClass() nhận được trạng thái của
đối tượng checkbox từ đặc tính DOM checked (được kiểm tra) và thiết lập lớp phong
cách của bảng panel, sử dụng đặc tính DOM className.
Liệt kê 6. Hàm
updatePanelClass() của SearchForm.jsp
function updatePanelClass()
{ var checkbox = document.getElementById("searchForm:moreOptions");
var panel = document.getElementById("searchForm:optionsPanel");
panel.className = checkbox.checked ? "visible" : "hidden"; } |
Tiêu
đề của trang SearchForm.jsp cũng chứa hàm initForm() (được hiển thị trong Liệt kê 7), cuộc
gọi của hàm này được mã hoá trong thuộc tính onload của phần tử <body>.
Hàm này định vị trường văn bản của biểu mẫu và gọi focus(), để khi trang Web được nạp trong trình duyệt này,
người sử dụng có thể bắt đầu nhập văn bản mà không cần phải nhấn chuột vào
thành phần đầu tiên. Sau đó, initForm() gọi
updatePanelClass() để khởi tạo lớp của ô
tùy
chọn.
Liệt kê 7. Hàm initForm () của SearchForm.jsp
function initForm()
{ var text = document.getElementById("searchForm:text");
text.focus();
updatePanelClass(); } |
Các
giá trị của các thành phần đầu vào từ ví dụ SearchForm.jsp được liên kết với các đặc tính của một bean đơn
giản và nút Search (Tìm kiếm) khởi động một phương thức hành động tên là
searchAction(). Mã của lớp SearchBean được hiển thị trong Liệt kê
8.
Liệt kê 8. Lớp
SearchBean
package jsfcssjs; public class SearchBean
implements java.io.Serializable {
private String text;
private boolean moreOptions;
private boolean matchCase;
private String language;
public SearchBean() { }
public String getText()
{ return text; }
public void setText(String text)
{ this.text = text; } ...
public String searchAction()
{ System.out.print("Text: " + text);
if (moreOptions)
{ if (matchCase) System.out.print(", Match Case");
System.out.print(", Language: " + language); }
System.out.println(); return null; }
} |
Thực hiện xác nhận hợp lệ phía máy khách
Khung công tác JSF cung cấp một số trình xác nhận hợp lệ (validator) chạy trên phía máy chủ. Khi một trình xác nhận hợp lệ JSF tìm thấy một lỗi, biểu mẫu được trả về cho người sử dụng để giúp cho họ có thể sửa chữa nó. Để giảm thiểu các việc gửi đi các biểu mẫu lỗi, bạn cũng có thể sử dụng mã JavaScript để kiểm tra đầu vào người dùng ở phía máy khách. Ví dụ sau cho thấy cách kiểm tra độ dài tối đa của dữ liệu nhập vào trong một thành phần vùng văn bản. Chiều dài hiện tại cũng được hiển thị khi người dùng gõ vào văn bản. Khi người dùng nhấn vào nút Submit (Gửi đi), một hàm JavaScript được sử dụng để kiểm tra giá trị nhập vào.
Ví dụ ClientValidation
Biểu mẫu JSF của ví dụ ClientValidation.jsp (xem Liệt kê 9) có chứa một thành phần
<h:inputTextarea> sử dụng thuộc tính
onkeyup, do đó, một hàm JavaScript tên là
validateText() được gọi mỗi khi người dùng
đã nhấn một phím. Hàm này cũng được gọi sau khi trang Web được nạp trong
trình duyệt vì thuộc tính onload của thẻ <body> chứa cuộc gọi validateText(). Ngay cả khi đầu vào người dùng được xác nhận
hợp lệ trên phía máy khách, thành phần <h:inputTextarea> cũng sử dụng thuộc tính required và trình xác nhận hợp lệ <f:validateLength> để kiểm tra văn bản đã
nhập vào trên máy chủ — chỉ trong trường hợp JavaScript bị
vô hiệu hóa trong trình duyệt của người dùng. Việc xác nhận hợp lệ phía
máy chủ cũng cần thiết để bảo vệ chống lại những người dùng cố tình gây
hại.
Liệt kê 9. Ví dụ
ClientValidation.jsp
<%@ taglib prefix="f"
uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="h"
uri="http://java.sun.com/jsf/html" %>
<f:view> <html> ... <body onload="validateText()">
<h1>Client-Side Validation</h1>
<h:form id="validForm" onsubmit="return validateForm()">
<h:panelGrid columns="1" border="0" cellspacing="5">
<h:panelGroup>
<h:outputText value="Text (max #{textBean.maxLength} chars): "/>
<f:verbatim><span id="charCount"></span>
</f:verbatim>
</h:panelGroup>
<h:panelGroup>
<h:inputTextarea
id="textArea" value="#{textBean.text}" required="true" rows="5" cols="30"
onkeyup="validateText()">
<f:validateLength maximum="#{textBean.maxLength}"/>
</h:inputTextarea>
<h:message for="textArea"/>
</h:panelGroup>
<h:commandButton id="submit" value="Submit" action="#{textBean.submitAction}"/>
</h:panelGrid>
</h:form>
</body>
</html>
</f:view> |
Hàm
validateText() (được thể hiện trong Liệt kê
10) định vị biểu mẫu của trang bằng document.forms.validForm và nhận được đối tượng biểu diễn vùng
văn bản với form.elements["validForm:textArea"]. Sau đó, mã JavaScript kiểm
tra chiều dài của văn bản đã nhập vào và trả về một thông báo lỗi nếu đầu
vào của người dùng không hợp lệ. Ngoài ra, validateText() chỉ ra chiều dài hiện tại bằng cách thiết lập
đặc tính innerHTML của phần tử <span id="charCount"> từ ClientValidation.jsp. Lớp phong cách này cũng
được cập nhật, sử dụng đặc tính DOM className
của phần tử span.
Liệt kê 10. Hàm validateText () của ClientValidation.jsp
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<f:view> <html> <head>
<title>Client-Side Validation </title>
<style type="text/css"> .valid {color: green; } .error { color: red; }
</style>
<script
type="text/javascript">
function validateText() {
var form = document.forms.validForm;
var textArea = form.elements["validForm:textArea"];
var length = textArea.value.length;
var maxLength = <h:outputTextvalue="#{textBean.maxLength}"/>;
var error = null;
if (length == 0) error = "Text cannot be empty.";
else if (length > maxLength) error = "Text cannot have more than "
+ maxLength + " characters."
var span = document.getElementById("charCount");
span.innerHTML = length;
span.className = (error == null) ? "valid" : "error";
return error; } ... </script>
</head> ... </html> </f:view> |
Biến
maxLength được khởi tạo trong mã JavaScript
từ Liệt kê 10, sử dụng đặc tính có cùng tên của một bean có mã nhận dạng
của nó là textBean. Giá trị của đặc tính này
được xác định trong tệp faces-config.xml (xem
Liệt kê
11).
Liệt kê 11. Cấu hình TextBean trong faces-config.xml
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" ... version="1.2">
<managed-bean>
<managed-bean-name>textBean</managed-bean-name>
<managed-bean-class>jsfcssjs.TextBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>maxLength</property-name>
<value>100</value> </managed-property>
</managed-bean> ...
</faces-config> |
Ví
dụ ClientValidation.jsp có chứa một hàm
JavaScript thứ hai có tên là validateForm()
(xem Liệt kê 12). Hàm này được gọi khi nhấn nút Submit. Quan sát từ
khóa return (trả về) được sử dụng trong thuộc
tính của onsubmit của <h:form>, ngay trước cuộc gọi validateForm(). Nếu giá trị được trả về là false (sai), nghĩa là đầu vào của người sử dụng không hợp lệ,
trình duyệt Web không gửi dữ liệu biểu mẫu đến máy chủ. Đây là kết quả
mong muốn, vì validateForm() báo hiệu lỗi cho
người dùng bằng alert(), và không có lý do để
gửi đi đầu vào không hợp
lệ.
Liệt kê 12. Hàm validateForm () của ClientValidation.jsp
function validateForm()
{ var error = validateText();
if (error) alert(error);
return error == null; } |
Nếu
đầu vào của người dùng hợp lệ, validateForm()
trả về true (đúng) và trình duyệt Web gửi đi dữ
liệu biểu mẫu đến máy chủ, ở đó khung công tác JSF gọi phương thức submitAction() của lớp TextBean (xem Liệt kê
13).
Liệt kê 13. Lớp
TextBean
package jsfcssjs; public class TextBean
implements java.io.Serializable { private String text; private int maxLength; ...
public String submitAction() { System.out.println("Length: " + text.length());
return null; } } |
Việc
xác nhận chiều dài tối đa hợp lệ đã thực hiện ở trên cho thành phần <h:inputTextarea> được mã hóa trong ví dụ
ClientValidation.jsp. Đây là cách đơn giản
nhất để làm điều đó, nhưng mã không thể sử dụng lại mà không cần thay đổi.
Khi triển khai thực hiện các tính năng không phải là đặc trưng cho một
trang cụ thể, ý tưởng tốt là di chuyển mã JavaScript vào trong các tệp
.js bên ngoài để bạn có thể gọi các hàm của
chúng từ bất kỳ trang ứng dụng nào. Ngoài ra, bạn có thể phát triển các
thành phần JSF tùy chỉnh để thiết lập các cơ chế dựa trên JavaScript và
thậm chí bạn có thể thêm các thuộc tính tùy chỉnh tới các thành phần hiện
có. Phần tiếp theo cho thấy cách thực hiện các tính năng UI chung để nâng
cao các thành phần JSF chuẩn.
Sử dụng các thuộc tính tuỳ chỉnh để kích hoạt các tính năng UI mới
Phần 1 của loạt bài này đã chỉ cho bạn cách xây dựng một thành
phần tuỳ chỉnh để thiết lập các phong cách mặc định của các thành phần
JSF. Trong phần này, bạn sẽ thấy cách sử dụng các kỹ thuật tương tự để
thiết lập thuộc tính liên quan đến JavaScript. Ngoài ra, các ví dụ mẫu
được trình bày ở đây sử dụng một thuộc tính tuỳ chỉnh, được bổ sung thêm
bằng thẻ <f:attribute> cho các thành phần
JSF chuẩn.
Phát triển các thành phần JSF tùy chỉnh
Tiếp theo, bạn sẽ
thấy cách xây dựng một thành phần tùy chỉnh có sửa đổi các thuộc tính
onfocus và onblur của mọi thành phần lồng nhau có chứa <f:attribute name="helpOnFocus"
value="..."> (xem Liệt kê 14). Các biểu thức JavaScript của
các thuộc tính onfocus và onblur được đánh giá trong trình duyệt Web khi một phần tử
biểu mẫu tập trung hay không tập trung vào bàn phím. Thành phần tùy chỉnh,
mà thẻ của nó có tên <js:helpOnFocus>,
thay đổi thuộc tính onfocus của mỗi thành phần
đầu vào lồng nhau để chỉ ra một thông báo trợ giúp trong một phần tử <div id="helpOnFocus"> khi phần tử <input> được hoàn trả tập trung chú ý.
Thuộc tính onblur của thành phần lồng nhau cũng
được thay đổi để xóa thông báo trợ giúp khi các phần tử <input> không tập trung vào bàn
phím.
Liệt kê 14. Sử dụng thành phần <js:helpOnFocus> trong một trang JSF
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib
prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib
prefix="js" uri="/js.tld" %> ... <script type="text/javascript"
src=".../HelpOnFocus.js"> </script> ...
<js:helpOnFocus> ... <h:inputText ...>
<f:attribute name="helpOnFocus" value="... Help message ..."/>
</h:inputText> ... </js:helpOnFocus> ... <div
id="helpOnFocus"> </div> ... |
Lớp
SetupComponent, được trình bày trong Phần 1, duyệt cây thành phần JSF, cho phép bạn thay đổi các thuộc
tính của các thành phần lồng nhau ngay trước khi hoàn trả chúng. Lớp HelpOnFocusComponent (xem Liệt kê 15) mở rộng
SetupComponent và thực hiện phương thức
setup(), được gọi cho mọi thành phần lồng
nhau có đặc tính rendered là true. Phương thức getAttribute() lấy giá trị của một thuộc tính từ bản đồ thuộc
tính của thành phần. Phương thức insertCall()
gồm có một cuộc gọi hàm trong thuộc tính đã cho, bảo toàn giá trị hiện có
bất kỳ. Không có gì được thực hiện nếu thuộc tính đã có chứa cuộc gọi hàm,
cuộc gọi này xảy ra khi ứng dụng trả về biểu mẫu tương tự đến người sử
dụng sau khi gửi thông báo đi. Trong trường hợp này, thành phần tuỳ chỉnh
đã thêm các thuộc tính onfocus và onblur tới các thành phần lồng nhau trong lúc xử
lý một yêu cầu trước đó. Các thuộc tính đã có chứa các cuộc gọi JavaScript
vì khung công tác JSF lưu trữ trạng thái của tất cả các thành phần giữa
các yêu
cầu.
Liệt kê 15. Lớp
HelpOnFocusComponent
package jsfcssjs; import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.util.Map;
public class HelpOnFocusComponent extends SetupComponent
{ protected void setup(FacesContext ctx, UIComponent comp)
{ Map<String, Object>
attrMap = comp.getAttributes();
String helpOnFocus = getAttribute(attrMap, "helpOnFocus");
if (helpOnFocus != null)
{ String helpParam[] = new String[]
{ EncodeUtils.encodeString(helpOnFocus).toString() };
insertCall(attrMap, "onfocus", "showHelpOnFocus", helpParam);
insertCall(attrMap, "onblur", "clearHelpOnBlur", null); } }
protected String getAttribute(Map<String, Object> attrMap, String attrName)
{ Object attrValue = attrMap.get(attrName);
if (attrValue != null) return attrValue.toString();
else return null; }
protected void insertCall(Map<String, Object> attrMap,
String attrName, String functionName, String functionParams[])
{ String attrValue = getAttribute(attrMap, attrName);
if (attrValue != null && attrValue.indexOf(functionName) != -1) return;
StringBuilder buf = EncodeUtils.encodeCall( functionName, functionParams);
if (attrValue != null && attrValue.length()
> 0) { buf.append(';'); buf.append(attrValue);
}
attrMap.put(attrName, buf.toString());
}
public String getFamily() { return "HelpOnFocus"; } } |
Tệp
HelpOnFocus.js (hiển thị trong Liệt kê 16)
chứa các hàm showHelpOnFocus() và clearHelpOnBlur() mà những lần gọi của các hàm
này được mã hoá trong các thuộc tính onfocus và
onblur. Hàm setInnerHTML() chèn content (nội
dung) nhất định vào thành phần HTML với id đã
định.
Liệt kê 16. Tệp HelpOnFocus.js
function setInnerHTML(id, content) {document.getElementById(id).innerHTML = content;}
function showHelpOnFocus(msg) {setInnerHTML("helpOnFocus", msg); }
function clearHelpOnBlur() { setInnerHTML("helpOnFocus", ""); } |
Giống
như bất kỳ thành phần JSF tùy chỉnh nào, HelpOnFocusComponent phải được cấu hình trong faces-config.xml (xem Liệt kê
17).
Liệt kê 17. Cấu hình HelpOnFocusComponent trong faces-config.xml
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" ... version="1.2"> ... <component> <component-type>HelpOnFocusComponent</component-type> <component-class>jsfcssjs.HelpOnFocusComponent</component-class> <component-extension> <component-family>HelpOnFocus</component-family> </component-extension> </component> </faces-config> |
Liệt
kê 18 chỉ ra lớp HelpOnFocusTag, lớp này thực
hiện trình xử lý thẻ cho thành phần tùy chỉnh. Lớp cơ sở UIComponentELTag là một phần của API của phiên
bản JSF 1.2. Nếu bạn muốn sử dụng thành phần tùy chỉnh này trong một ứng
dụng dựa vào JSF 1.1, siêu lớp này của trình xử lý thẻ phải là UIComponentTag.
Liệt kê 18.
Lớp HelpOnFocusTag
package jsfcssjs;
import javax.faces.webapp.UIComponentELTag;
public class HelpOnFocusTag extends UIComponentELTag
{ public String getComponentType() { return "HelpOnFocusComponent"; }
public String getRendererType() { return null; } } |
Tên
và các thuộc tính của thẻ tùy chỉnh được định rõ trong tệp js.tld (được hiển thị trong Liệt kê 19). Trình xử
lý HelpOnFocusTag không có bất cứ phương thức
setter nào cho các thuộc tính của nó vì nó thừa kế chúng từ UIComponentELTag.
Liệt kê 19. Tệp js.tld
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" ... version="2.1"> <tlib-version>1.0</tlib-version> <short-name>js</short-name> <uri>/js.tld</uri> <tag> <name>helpOnFocus</name> <tag-class>jsfcssjs.HelpOnFocusTag</tag-class> <body-content>JSP</body-content> <attribute> <name>id</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>binding</name> <required>false</required> <deferred-value> <type>jsfcssjs.HelpOnFocusComponent</type> </deferred-value> </attribute> <attribute> <name>rendered</name> <required>false</required> <deferred-value> <type>boolean</type> </deferred-value> </attribute> </tag> </taglib> |
Các tiện ích mã hóa JavaScript
Lớp HelpOnFocusComponent
đã trình bày ở trên sử dụng các phương thức của trình trợ giúp (helper) do
lớp EncodeUtils cung cấp. Phương thức encodeString() (xem Liệt kê 20) thoát khỏi dấu
cách, TAB, CR, LF và các ký tự không phải ASCII để có thể sử dụng giá trị
được trả về như là một chuỗi chữ trong mã JavaScript. Ngoài ra, các ký tự
", &, <, và > được
thay thế bằng ", &, < và > để có thể bao gồm chuỗi đã mã hóa trong
các trang HTML. Các ký tự trong hai dấu nháy được sử dụng như các dấu phân
cách. Ví dụ, nếu bạn chuyển abc \ " & < >
[TAB] [LF] [CR] 123 qua encodeString(), giá trị trả lại sẽ là "abc
\\ " & < > \t \n \r
123".
Liệt kê 20. Phương thức
encodeString() của lớp EncodeUtils
package jsfcssjs;
public class EncodeUtils { public static StringBuilder encodeString(String str)
{ if (str == null) return null;
StringBuilder buf = new StringBuilder();
buf.append('"');
int n = str.length();
for (int i = 0; i < n; i++)
{ char ch = str.charAt(i);
switch (ch) { case '\\': buf.append("\\\\"); break;
case '\'': buf.append("\\\'"); break;
case '"': buf.append("""); break;
case '&': buf.append("&"); break;
case '<': buf.append("<"); break;
case '>': buf.append(">"); break;
case '\t': buf.append("\\t"); break;
case '\r': buf.append("\\r"); break;
case '\n': buf.append("\\n"); break;
default:
{ if (' ' <= ch && ch <= '~') buf.append(ch);
else { buf.append("\\u");
for (int j = 3; j >= 0; j--)
{ int h = (((int) ch) >> (j*4)) & 0x0f;
buf.append((char) (h<10 ? '0'+h : 'a'+h-10)); }
}
}
}
}
buf.append('"');
return buf; } ... } |
Phương
thức encodeCall() (xem Liệt kê 21) xây dựng một
cuộc gọi hàm bằng cách sử dụng các tham số đã
cho.
Liệt kê 21. Phương thức encodeCall () của lớp
EncodeUtils
public class EncodeUtils
{... public static StringBuilder encodeCall( String functionName, String functionParams[])
{StringBuilder buf = new StringBuilder();
buf.append(functionName);
buf.append('(');
if (functionParams != null)
for (int i = 0; i < functionParams.length; i++)
{ if (i > 0) buf.append(',');
buf.append(functionParams[i]); }
buf.append(')'); return buf; } } |
Để
hiểu rõ hơn về các phương thức mã hóa, hãy xem xét ví dụ từ Liệt kê 22,
trong đó có chứa thành phần đầu vào đã có một thuộc tính onfocus. Thuộc tính helpOnFocus cũng được bổ sung vào thành phần này với <f:attribute> và biểu mẫu của nó được bao
bọc bằng một thành phần <js:helpOnFocus>.
Liệt kê 22. Ví dụ mã hóa chuỗi
<% request.setAttribute("msg", "abc \\ \" &
< > \t \n \r 123"); %> ... <js:helpOnFocus>
<h:form> <h:inputText ... onfocus="alert('focus')">
<f:attribute name="helpOnFocus" value="#{msg}"/>
</h:inputText> </h:form> </js:helpOnFocus>
... <div id="helpOnFocus"> </div> ... |
Khi
trang này được thực hiện, các thành phần <js:helpOnFocus> duyệt cây thành phần lồng nhau và thiết
lập các thuộc tính onfocus và onblur để hiển thị và xóa thông báo trợ giúp.
Liệt kê 23 cho thấy HTML được các thành phần JSF hoàn trả. Thuộc tính
onfocus chứa cuộc gọi showHelpOnFocus() cùng với biểu thức alert('focus') từ ví dụ được hiển thị trong Liệt kê 22. Các ký
tự & và dấu phân cách " của chuỗi đã mã hóa được thoát khỏi chương trình thêm một
thời gian nhờ thành phần <h:inputText>,
nó mã hóa các giá trị của các thuộc tính riêng của
mình.
Liệt kê 23. Chuỗi được mã hóa
<form ...> ... <input ...
onblur="clearHelpOnBlur()" onfocus="showHelpOnFocus("abc \\
&quot; &amp; &lt; &gt; \t \n \r 123");
alert('focus')" /> ... </form> ...
<div id="helpOnFocus">
</div> ... |
Khi
trình duyệt Web phân tích cú pháp và giải mã đoạn HTML từ Liệt kê 23, giá
trị được lấy từ thuộc tính onfocus là showHelpOnFocus("abc \\ " & <
> \t \n \r 123");alert('focus'). Trình duyệt chuyển mã
này tới công cụ (engine) JavaScript của nó, công cụ này gọi hàm showHelpOnFocus() để chèn abc
\ " & < > [TAB] [NL] [CR] 123
trong phần tử <div id="helpOnFocus"> của
trang Web và trình duyệt này cho thấy abc \ " &
< > 123.
Sử dụng thành phần tùy chỉnh trong một trang JSF
Trang HelpOnFocus.jsp (xem Liệt kê 24) là một ví dụ khác có sử dụng
các thành phần <js:helpOnFocus>. Biểu mẫu
đầu vào JSF chứa ba thành phần: một trường văn bản, một hộp kiểm tra và
một danh sách thả xuống. Tất cả các thành phần này sử dụng thẻ <f:attribute> để xác định các thông báo trợ
giúp.
Liệt kê 24. Ví dụ
HelpOnFocus.jsp <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="js" uri="/js.tld" %> <f:view> ... <script type="text/javascript" src="<%=request.getContextPath()%>/HelpOnFocus.js"> </script> ... <js:helpOnFocus> ... <h:inputText id="text" ...> <f:attribute name="helpOnFocus" value="Enter the text you want to search for."/> </h:inputText> ... <h:selectBooleanCheckbox id="matchCase" ...> <f:attribute name="helpOnFocus" value="Distinguish between lowercase and uppercase characters."/> </h:selectBooleanCheckbox> ... <h:selectOneMenu id="language" ...> ... <f:attribute name="helpOnFocus" value="Select the language of the searched text."/> </h:selectOneMenu> ... <f:verbatim> <div id="helpOnFocus"> </div> </f:verbatim> ... </js:helpOnFocus> ... </f:view> |
Liệt
kê 25 hiển thị HTML được trang HelpOnFocus.jsp
tạo
ra.
Liệt kê 25.
HelpOnFocus.jsp tạo ra HTML ... <script type="text/javascript" src="/jsf12js/HelpOnFocus.js"> </script> ... <input id="searchForm:text" type="text" ... onblur="clearHelpOnBlur()" onfocus="showHelpOnFocus("Enter the text you want to search for.")"/> ... <input id="searchForm:matchCase" type="checkbox" ... onblur="clearHelpOnBlur()" onfocus="showHelpOnFocus("Distinguish between lowercase and uppercase \r\n characters.")" /> ... <select id="searchForm:language" ... onblur="clearHelpOnBlur()" onfocus="showHelpOnFocus("Select the language of the searched text.")"> ... </select> ... <div id="helpOnFocus"> </div> ... |
Bài này đã cho bạn thấy làm thế nào để phát triển các trình xử lý sự kiện JavaScript để cập nhật HTML được các thành phần JSF hoàn trả. Nó giới thiệu một số kỹ thuật Web, bao gồm:
- Thiết lập thuộc tính
classNameđể thay đổi giá trị của thuộc tínhclass. - Sử dụng đặc tính
innerHTMLđể chèn một số nội dung trong một phần tử HTML. - Ẩn và hiển thị các thành phần JSF với CSS.
- Thực hiện xác nhận hợp lệ phía máy khách với JavaScript.
Nó cũng cho bạn thấy cách sử dụng thuộc tính tùy chỉnh kết hợp với một thành phần của trình bao bọc (wrapper) để thêm tính năng mới cho các thành phần JSF hiện tại.
| Mô tả | Tên | Kích thước | Phương thức tải |
|---|---|---|---|
| Sample application for this article | jsfcssjs_p2.zip | 30KB | HTTP |
Học tập
- "Các ứng dụng Craft Ajax sử dụng JSF với CSS và Javascript, Phần
1" giới thiệu sự hỗ trợ CSS được các thành phần JSF tiêu chuẩn cung
cấp. Nó cũng giải thích các cây thành phần JSF được xây dựng như thế nào
và bạn có thể thêm các tính năng như các phong cách mặc định như thế
nào.
- Tự động lưu các biểu mẫu JSF bằng Ajax là một loạt bài ba phần
trình bày các kỹ thuật JavaScript và JSF bổ sung.
-
Vùng phát
triển Web của developerWorks được đóng gói với các công cụ và
thông tin để phát triển Web 2.0.
-
Trung
tâm tài nguyên Ajax của developerWorks chứa một thư viện phát
triển về nội dung Ajax cũng như nguồn tài nguyên có ích để giúp bạn bắt
đầu phát triển các ứng dụng Ajax ngày hôm nay.
-
Đặc tả JSF 1.2 mô tả đầy đủ công nghệ JavaServer Faces.
- Truy cập developer.mozilla.org nếu bạn đang tìm kiếm các công cụ và tài
liệu JavaScript.
- Bạn có thể tìm thêm tài nguyên liên quan
đến CSS trên trang chủ Cascading
Style Sheets.
Lấy sản phẩm và công nghệ
- Nhận được các đặc
tả DOM Core và DOM HTML từ www.w3C.org
- Nhận được mã
nguồn JSF RI và các mã nhị phân từ javaserverfaces.dev.java.net
- Dự án Apache MyFaces là một cách
thực hiện JSF phổ biến khác.
Thảo luận
Andrei Cioroianu là người sáng lập của Devsphere, một nhà cung cấp các dịch vụ phát triển Java EE và các dịch vụ tư vấn Ajax / JSF. Ông đã sử dụng các công nghệ Java và Web từ năm 1997 và có 10 năm kinh nghiệm chuyên môn trong việc giải quyết những vấn đề kỹ thuật phức tạp và quản lý toàn bộ vòng đời của các sản phẩm thương mại, các ứng dụng tùy chỉnh và các khung công tác mã nguồn mở