Trong bài viết này — bài thứ hai trong loạt bài ba phần về các đặc tính mới của JSF 2 — David Geary, thành viên nhóm chuyên gia JSF 2.0 cho bạn thấy các ứng dụng Web của mình có thể tận
Trang 1Sức mạnh của JSF 2, Phần 2: Tạo khuôn mẫu và các thành phần phức hợp
Triển khai các giao diện người dùng mở rộng với JavaServer Faces 2
David Geary, Chủ tịch, Clarity Training, Inc
Tóm tắt: Java™Server Faces (JSF) 2 cho phép bạn triển khai thực hiện các giao
diện người dùng (UI), dễ dàng sửa đổi và mở rộng với hai tính năng mạnh mẽ: tạo khuôn mẫu và các thành phần phức hợp Trong bài viết này — bài thứ hai trong loạt bài ba phần về các đặc tính mới của JSF 2 — David Geary, thành viên nhóm chuyên gia JSF 2.0 cho bạn thấy các ứng dụng Web của mình có thể tận dụng tốt nhất việc tạo khuôn mẫu và các thành phần phức hợp như thế nào
Trở lại năm 2000, khi tôi tham gia vào một danh sách gửi thư về JavaServer Pages (JSP), tôi đã gặp Craig McClanahan, người đang làm việc cho một khung công tác Web mới sinh ra có tên là Struts Quay lại thời đó, khi di chuyển từ Swing sang lập trình Java phía máy chủ, tôi đã thực hiện một khung công tác nhỏ để tách biệt cách bài trí khung nhìn của JSP khỏi nội dung của nó, tương tự với tinh thần của các trình quản lý cách bài trí của Swing Craig đã hỏi tôi có muốn cho phép đưa
thư viện tạo khuôn mẫu của tôi vào trong Struts không và tôi đã vui lòng đồng ý
Thư viện khuôn mẫu của Struts (Struts Template Library), được đóng gói cùng với Struts 1.0, đã trở thành cơ sở cho thư viện Tiles phổ biến của Strut, để cuối cùng thư viện này đã trở thành một khung công tác Apache hàng đầu
Hiện nay, công nghệ hiển thị mặc định của JSF 2 — Facelets — là một khung công tác tạo khuôn mẫu phần lớn dựa vào Tiles JSF 2 cũng cung cấp một cơ chế
mạnh, được gọi là các thành phần phức hợp, xây dựng trên các đặc tính tạo khuôn
mẫu của Facelets để cho bạn có thể triển khai thực hiện các thành phần tùy chỉnh không cần mã Java và không có cấu hình XML nào Trong bài này, tôi sẽ giới thiệu cho bạn về tạo khuôn mẫu và các thành phần phức hợp với ba lời khuyên để khai thác tốt nhất JSF 2:
Lời khuyên 1: Giữ nguyên tắc DRY (Đừng lặp lại chính mình)
Lời khuyên 2: Hãy dùng cách hợp thành
Lời khuyên 3: Hãy nghĩ theo kiểu LEGO
Facelets và JSF 2
Trong khi chuẩn hoá việc thực hiện Facelets mã nguồn mở, Nhóm chuyên gia JSF
2 (JSF 2 Expert Group) đã thực hiện một số thay đổi cho các API Facelets bên
Trang 2dưới nhưng giữ lại khả năng tương thích lùi với thư viện thẻ Điều đó có nghĩa là các khung nhìn hiện có, được triển khai thực hiện bằng phiên bản mã nguồn mở của Facelets sẽ làm việc được với JSF 2
Bạn có thể tìm hiểu thêm về nhiều tính năng của Facelets trong bài viết của Rick Hightower "Facelets vừa khít với JSF" (Facelets Fits JSF like a Glove) và "Lập trình Facelets nâng cao (Advanced Facelets programming)."
Lời khuyên 1: Giữ nguyên tắc DRY
Trong công việc đầu tiên của tôi với vai trò nhà phát triển phần mềm, tôi đã triển khai thực hiện một giao diện người dùng đồ họa (GUI) cho các hệ thống thiết kế
có hỗ trợ của máy tính và hệ thống chế tác có hỗ trợ của máy tính (CAD/CAM) dựa trên UNIX®
Ban đầu tất cả đã diễn ra khá tốt, nhưng theo thời gian, mã của tôi đã trở nên ngày càng có nhiều vấn đề Vào lúc chúng tôi phát hành, hệ thống đã dễ mắc lỗi đến mức tôi đã sợ hãi việc sửa lỗi và bản phát hành gây ra ngay một luồng tới tấp các báo cáo lỗi
Nếu tôi đã theo nguyên tắc DRY — Đừng lặp lại chính mình — khi làm dự án đó, thì tôi có thể đã đã bớt được cho mình nhiều sự phiền toái Nguyên tắc DRY, do Dave Thomas và Andy Hunt đặt ra (xem Tài nguyên), nói rõ:
Mỗi một mảnh kiến thức phải được biểu diễn duy nhất, không lập lờ và chính thức trong một hệ thống
Ứng dụng CAD/CAM của tôi không theo nguyên tắc DRY — nó đã có quá nhiều
sự kết dính giữa các mối quan tâm — vì vậy các thay đổi trong một khu vực đã gây ra những thay đổi bất ngờ ở nơi khác
JSF 1 đã vi phạm nguyên tắc DRY trong một số khía cạnh, ví dụ bằng cách buộc bạn cung cấp hai biểu diễn của bean được quản lý của bạn — một trong XML và một trong mã Java Nhu cầu có nhiều biểu diễn đã làm cho khó khăn hơn khi tạo
ra và thay đổi bean được quản lý Như tôi đã chỉ ra cho bạn trong Phần 1, JSF 2 cho phép bạn sử dụng các chú giải thay vì XML để cấu hình bean được quản lý, cho bạn một cách biểu diễn duy nhất, chính thức của bean được quản lý của bạn
Gác lại Bean được quản lý sang một bên, ngay cả các thói quen hình như vô hại — chẳng hạn như bao gồm bảng định kiểu (stylesheet) giống nhau trong tất cả các khung nhìn của bạn — cũng vi phạm nguyên tắc DRY và có thể gây ra lo lắng Nếu bạn thay đổi tên của bảng định kiểu, bạn phải thay đổi nhiều khung nhìn Tốt hơn là gói kín việc bao gồm thêm bảng định kiểu nếu bạn có thể làm được
Trang 3Nguyên tắc DRY cũng áp dụng để thiết kế mã của bạn Nếu bạn có nhiều phương thức mà tất cả đều chứa mã để duyệt đi qua một cây chẳng hạn, thì một ý tưởng tốt
sẽ là gói kín thuật toán duyệt đi qua một cây, có thể là trong một lớp con
Giữ nguyên tắc DRY đặc biệt quan trọng khi bạn thực hiện một giao diện người dùng, là nơi (người ta cho rằng) những thay đổi hay xảy ra nhất trong quá trình phát triển
Tạo khuôn mẫu JSF 2
Một trong nhiều cách trong đó JSF 2 hỗ trợ nguyên tắc DRY là tạo khuôn mẫu
(templating) Các khuôn mẫu bao gói các chức năng phổ biến trong số các khung nhìn trong ứng dụng của bạn, vì vậy bạn cần phải xác định chức năng đó chỉ một
lần Một khuôn mẫu được sử dụng bởi nhiều cấu kiện để tạo ra các khung nhìn
Liệt kê 1 Khuôn mẫu các địa điểm: /templates/masterLayout.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
Trang 4<h:outputScript library="javascript" name="util.js" target="head"/>
<h:outputStylesheet library="css" name="styles.css" target="body"/>
Trang 5 Các thẻ HTML <head>, <body> và <title>
Một tiêu đề (title) mặc định (có thể bị ghi đè bởi các cấu kiện sử dụng khuôn mẫu)
Một bảng định kiểu CSS
Trang 6 Một số tiện ích JavaScript
Một cách bố trí dưới dạng các <div> và các lớp CSS tương ứng
Nội dung mặc định cho phần đầu (head) (có thể bị ghi đè)
Nội dung mặc định cho trình đơn bên phải (có thể bị ghi đè)
Như Liệt kê 1 minh họa, các khuôn mẫu chèn nội dung vào trong cách bố trí của chúng bằng thẻ <ui:insert>
Nếu bạn quy định một phần thân cho một thẻ <ui:insert>, như tôi đã thực hiện đối với tiêu đề cửa sổ, phần đầu và trình đơn bên phải trong Liệt kê 1, JSF sử dụng
phần thân của thẻ như nội dung mặc định Các cấu kiện sử dụng khuôn mẫu có thể
định nghĩa nội dung hoặc ghi đè lên nội dung mặc định bằng thẻ <ui:define>, như
đã chứng tỏ trong Liệt kê 2, hiển thị tài liệu đánh dấu siêu văn bản của khung nhìn đăng nhập:
Liệt kê 2 Khung nhìn đăng nhập
Trang 7
</ui:composition>
Khung nhìn đăng nhập sử dụng nội dung mặc định của khuôn mẫu cho tiêu đề cửa
sổ, phần đầu và trình đơn bên phải Nó chỉ định nghĩa chức năng cụ thể riêng cho khung nhìn đăng nhập: phần nội dung và trình đơn bên trái
Bằng cách cung cấp một thẻ <ui:define> cho tiêu đề cửa sổ, phần đầu hoặc trình đơn bên phải, tôi có thể đã ghi đè nội dung mặc định do khuôn mẫu định nghĩa Ví
dụ, Liệt kê 3 cho thấy khung nhìn xem mã nguồn (hình ảnh ở giữa trong Hình 1):
Liệt kê 3 Khung nhìn xem-mã nguồn
Trang 8Liệt kê 4 cho thấy khung nhìn các địa điểm (hình dưới cùng trong Hình 1):
Liệt kê 4 Khung nhìn các địa điểm
Trang 9</ui:define>
</ui:composition>
Tạo khuôn mẫu JSF 2
Khái niệm phía sau tạo khuôn mẫu là đơn giản Bạn định nghĩa chỉ một khuôn mẫu bao gói các chức năng phổ biến trong nhiều khung nhìn Mỗi khung nhìn bao gồm một cấu kiện và một khuôn mẫu
Khi JSF tạo một khung nhìn, nó nạp khuôn mẫu của cấu kiện và sau đó chèn nội dung được định nghĩa bởi cấu kiện đó vào khuôn mẫu đó
Chú ý các nét giống nhau trong các Liệt kê 2, 3 và 4 Cả ba khung nhìn xác định rõ khuôn mẫu của chúng và định nghĩa nội dung Cũng để ý việc tạo các khung nhìn mới dễ dàng như thế nào, bởi vì hầu hết cơ sở hạ tầng của khung nhìn được bao gói trong một khuôn mẫu và các tệp bao gồm thêm
Một khía cạnh thú vị khác khi sử dụng tạo khuôn mẫu JSF là các khung nhìn giống như các khung nhìn trong các Liệt kê 2, 3 và 4 không thay đổi nhiều theo thời gian, vì vậy một phần đáng kể của mã khung nhìn của bạn về cơ bản không yêu cầu phải bảo trì
Cũng giống như các khung nhìn có sử dụng chúng, các khuôn mẫu cũng có xu hướng rất ít thay đổi Và vì bạn đã bao gói rất nhiều chức năng phổ biến như vậy vào trong các mã lệnh hầu như không cần bảo trì, bạn có thể tập trung vào nội dung thực tế của khung nhìn của bạn — những gì diễn ra trong trình đơn bên trái của trang đăng nhập chẳng hạn Việc tập trung vào nội dung thực tế của khung nhìn của bạn là tất cả những gì mà lời khuyên tiếp theo bàn đến
Lời khuyên 2: Hãy dùng cách hợp thành
Không lâu sau khi bản GUI CAD/CAM của tôi đã được phát hành, tôi đã dành một vài tháng tiếp tục làm việc với một nhà phát triển có tên là Bob, trong một dự
Trang 10án không có liên quan gì Chúng tôi đã làm việc trên cơ sở mã của Bob và chúng tôi đã có thể thay đổi và sửa lỗi dễ dàng đến không ngờ
Tôi sớm đã nhận ra rằng chỉ có một sự khác biệt lớn nhất giữa mã của Bob và của
tôi là ông đã viết các phương thức rất nhỏ — thường là từ 5 đến 15 dòng mã — và
toàn bộ hệ thống của ông đã được lắp ráp với nhau từ các phương thức rất nhỏ đó Trong khi mà tôi đánh vật để sửa đổi các phương thức dài, làm nhiều việc trong dự
án trước đây của tôi, Bob nhanh nhẹn phối hợp các phương thức nhỏ với chức năng nguyên tử (atomic - không chia nhỏ được nữa) Sự khác biệt trong khả năng bảo trì và mở rộng được giữa mã của Bob và của tôi giống như so sánh đêm với ngày và từ đó trở đi, các phương thức rất nhỏ đã thuyết phục tôi
Cả Bob lẫn tôi vào lúc đó đều chưa biết, nhưng chúng tôi đã sử dụng một mẫu thiết kế từ Smalltalk có tên là Phương thức hợp thành (Composed Method) (xem Tài nguyên):
Hãy chia phần mềm của bạn thành các phương thức thực hiện một nhiệm vụ có thể nhận biết được, ở chỉ một mức trừu tượng
Những lợi ích của việc sử dụng mẫu hình Phương thức hợp thành là có tài liệu hướng dẫn tốt (Xem "Kiến trúc tiến hóa và thiết kế nổi dần: Phương thức hợp thành và SLAP" của Neal Ford với các giải thích chi tiết tuyệt vời) Ở đây, tôi sẽ tập trung vào cách bạn có thể sử dụng mẫu hình Phương thức hợp thành với các khung nhìn JSF của bạn như thế nào
JSF 2 khuyến khích bạn hợp thành khung nhìn của mình từ các mảnh khung nhìn nhỏ hơn Việc tạo khuôn mẫu bao gói các chức năng chung, do đó chia các khung nhìn của bạn thành các mảnh nhỏ hơn JSF 2 cũng cung cấp một thẻ <ui:include>, như tôi đã giải thích trong các liệt kê trước, cho phép bạn tiếp tục chia nhỏ hơn nữa các khung nhìn của mình thành mảnh chức năng nhỏ hơn Ví dụ, Hình 2 cho thấy trình đơn bên trái của trang đăng nhập của ứng các địa điểm:
Hình 2 Trình đơn bên trái của trang đăng nhập
Và Liệt kê 5 cho thấy tệp định nghĩa nội dung của trình đơn đó:
Trang 11Liệt kê 5 Triển khai thực hiện của trình đơn bên trái của khung nhìn đăng nhập
Hình 3 cho thấy trình đơn bên trái của khung nhìn các địa điểm:
Trang 12Hình 3 Trình đơn bên trái của khung nhìn các địa điểm
Triển khai thực hiện của trình đơn bên trái của khung nhìn các địa điểm được hiển thị trong Liệt kê 6
Liệt kê 6 Thực hiện trình đơn bên trái của khung nhìn các địa điểm
Trang 14Liệt kê 6 Liệt kê 6 thực hiện một biểu mẫu và nó sử dụng một thành phần biểu tượng (Tôi sẽ thảo luận thành phần biểu tượng đó trong mục Thành phần biểu tượng (The icon component), ngay sau đây Tạm thời bây giờ, chỉ cần biết rằng tác giả của trang có thể kết hợp một hình ảnh và một phương thức một với một biểu tượng) Hình ảnh cho biểu tượng đăng xuất được hiển thị ở dưới cùng của Hình 3
và phương thức của biểu tượng đăng xuất — places.logout() — được hiển thị trong Liệt kê 7:
Liệt kê 7 Phương thức Places.logout()
package com.clarity;
@ManagedBean()
@SessionScoped
public class Places {
private ArrayList<Place> places = null;
Trang 15fc.getELContext(), null, "user");
Liệt kê 8 Cấu trúc lại trình đơn bên trái của khung nhìn các địa điểm
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets">
<div class="placesSearchForm">
<div class="placesSearchFormHeading">
Trang 16Liệt kê 9 cho thấy tệp addressForm.xhtml:
Liệt kê 9 addressForm.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
Trang 17
#{msgs.city} <h:inputText value="#{place.city}" size="10"/> #{msgs.state} <h:inputText value="#{place.state}" size="3"/> #{msgs.zip} <h:inputText value="#{place.zip}" size="5"/>
Liệt kê 10 cho thấy tệp logoutIcon.xhtml:
Liệt kê 10 logoutIcon.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:util="http://java.sun.com/jsf/composite/components/util">
Trang 18dụ, Hình 4 cho thấy các tệp, tạo thành ba khung nhìn trong ứng dụng các địa điểm:
Hình 4 Khung nhìn của ứng dụng các địa điểm
Trang 19Ba thư mục mà tôi đã tạo — views (các khung nhìn), sections (các phần) và
templates (các khuôn mẫu) — có chứa hầu hết các tệp XHTML được sử dụng để triển khai thực hiện các khung nhìn của ứng dụng các địa điểm Bởi vì các tệp trong các thư mục views và templates hiếm khi thay đổi, nên tôi có thể tập trung vào thư mục sections Nếu tôi muốn thay đổi biểu tượng trong trình đơn bên trái của trang đăng nhập, ví dụ thế, tôi biết chính xác cần đi tới đâu:
sections/login/menuLeft.xhtml
Bạn có thể sử dụng bất kỳ cấu trúc thư mục nào bạn muốn để tổ chức các tệp XHTML của bạn Một cấu trúc hợp lý giúp cho dễ dàng xác định vị trí mã mà bạn cần phải sửa đổi hơn
Bên cạnh việc giữ vững nguyên tắc DRY và việc sử dụng mẫu hình Phương thức hợp thành, việc bao gói các chức năng trong các thành phần tùy chỉnh cũng là một
ý tưởng tốt Các thành phần là một cơ chế tái sử dụng mạnh mẽ và bạn nên tận dụng lợi thế của sức mạnh đó Không giống như JSF 1, JSF 2 giúp bạn dễ dàng triển khai thực hiện các thành phần tùy chỉnh
Lời khuyên 3: Hãy nghĩ theo kiểu LEGO
Khi còn là một cậu bé, tôi đã có hai đồ chơi ưa thích: một bộ hóa học và bộ
LEGO Cả hai đều cho tôi tạo ra nhiều thứ bằng cách kết hợp các khối xây dựng
cơ bản, điều mà đã trở thành một niềm đam mê suốt đời dưới cái vỏ phát triển phần mềm
Sức mạnh của JSF đã luôn ở trong mô hình thành phần của nó, nhưng sức mạnh
đó cho đến nay đã không được nhận biết hoàn toàn vì rất khó để triển khai thực hiện các thành phần tùy chỉnh với JSF 1 Bạn phải viết mã Java, chỉ rõ cấu hình XML và có một sự hiểu biết tốt về vòng đời của JSF Với JSF 2, bạn có thể triển khai thực hiện các thành phần tùy chỉnh:
Không cần cấu hình nào, XML hay cách nào khác
Không có mã Java
Các nhà phát triển có thể gắn chức năng cho nó
Và triển khai nóng khi được sửa đổi
Trang 20Phần còn lại của bài viết này, tôi sẽ hướng dẫn bạn thông qua việc thực hiện ba thành phần tùy chỉnh cho ứng dụng các địa điểm: một biểu tượng, một ô đăng nhập và một ô để hiển thị bản đồ và thông tin thời tiết của một địa chỉ Nhưng trước tiên, tôi sẽ cho bạn một tổng quan về các thành phần phức hợp của JSF 2 Thực hiện các thành phần tùy chỉnh
JSF 2 phối hợp việc tạo khuôn mẫu Facelets (Facelets templating), xử lý tài
nguyên (đã thảo luận trong Phần 1) và một quy ước đặt tên đơn giản để triển khai
thực hiện các thành phần phức hợp Các thành phần phức hợp, như tên gọi cho
thấy, cho phép bạn hợp thành một thành phần từ các thành phần hiện có
Bạn thực hiện các thành phần phức hợp bằng XHTML ở một nơi nào đó trong thư mục tài nguyên và liên kết chúng, thuần túy theo quy ước, tới một vùng tên và một thẻ Hình 5 cho thấy tôi đã tổ chức các thành phần phức hợp cho ứng dụng các địa điểm như thế nào:
Hình 5 Các thành phần của ứng dụng các địa điểm
Để sử dụng các thành phần phức hợp, bạn khai báo một vùng tên và sử dụng các thẻ Các vùng tên luôn luôn là http://java.sun.com/jsf/composite cộng với tên của thư mục trong đó có chứa các thành phần, dưới thư mục tài nguyên Tên của chính thành phần là tên của tệp XHTML của nó, không có phần mở rộng xhtml Quy ước này tránh được sự cần thiết phải có bất kỳ cấu hình nào Ví dụ, để sử dụng thành phần login (đăng nhập) trong ứng dụng các địa điểm, bạn sẽ làm như sau:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:util="http://java.sun.com/jsf/composite/component/util">