Tải bản đầy đủ (.pdf) (31 trang)

Các ứng dụng Craft Ajax sử dụng JSF với CSS và Javascript, Phần 2: Các biểu mẫu JSF động Khám phá hỗ trợ JavaScript trong các thành phần JSF chuẩn pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (293.81 KB, 31 trang )

Các ứng dụng Craft Ajax sử dụng JSF với CSS và Javascript, Phần 2: Các
biểu mẫu JSF động
Khám phá hỗ trợ JavaScript trong các thành phần JSF chuẩn
Andrei Cioroianu, Nhà phát triển và Tư vấn Java cao cấp, Devsphere
Tóm tắt: Trong bài viết đầu tiên của loạt bài hai phần này, tác giả và nhà phát
triển Java™ Andrei Cioroianu đã chỉ ra cách sử dụng các thuộc tính phong cách
của các thành phần JavaServer Faces (JSF) và cách thiết lập các giá trị mặc định
cho các thuộc tính đó. Trong bài viết thứ hai của loạt bài này, hãy tìm hiểu cách sử
dụng các thuộc tính liên quan đến JavaScript của các thành phần JSF chuẩn. Tìm
hiểu một số kỹ thuật Web dựa trên Document Object Model (DOM-Mô hình đối
tượng tài liệu), các API, JavaScript và Cascading Style Sheets (CSS-Các bảng
định kiểu nhiều tầng). Hãy xem cách ẩn dấu và hiển thị các thành phần JSF tùy
chọn mà không cần làm mới lại trang Web, cách thực hiện việc xác nhận hợp lệ
phía máy khách, việc xác nhận này được thực hiện trong trình duyệt Web, và cách
phát triển thành phần tùy chỉnh để hiển thị các thông báo trợ giúp cho các phần tử
đầu vào của một biểu mẫu Web.
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>

Apache MyFaces Tobago
Nếu bạn thích sử dụng các thành phần JSF thay cho các thẻ HTML, bạn có thể sử
dụng thành phần <tc:script> của MyFaces Tobago, để hoàn trả phần tử <script>
cho bạn.
Đặ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"
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=" %>
<%@ taglib prefix="h" uri=" %>
<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

Sử dụng thuộc tính maxlength
Thành phần <h:inputText> cho phép bạn giới hạn độ dài của dữ liệu nhập vào
bằng thuộc tính maxlength. (độ dài tối đa). Không giống như trường đầu vào một
dòng, thành phần <h:inputTextarea> đa dòng không có thuộc tính maxlength vì
thẻ <textarea> được hoàn trả của HTML không hỗ trợ nó.
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=" %> <%@ taglib prefix="h"
uri=" %>
<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=" %>
<%@ taglib prefix="h" uri=" %>
<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=" 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=" %> <%@ taglib
prefix="h" uri=" %> <%@ taglib
prefix="js" uri="/js.tld" %> <script type="text/javascript"
us.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=" 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=" 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 &quot;, &amp;,
&lt; và &gt; để 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 \\
&quot; &amp; &lt; &gt; \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("&quot;"); break;
case '&': buf.append("&amp;"); break;
case '<': buf.append("&lt;"); break;
case '>': buf.append("&gt;"); break;
case '\t': buf.append("\\t"); break;
case '\r': buf.append("\\r"); break;

×