Khởi đầu với JavaServer Faces 1.2, Phần 1: Xây dựng các ứng dụng cơ bản

Công nghệ Java™Server Faces (JSF), một khung công tác phía máy chủ cung cấp một cách tiếp cận dựa vào thành phần để việc phát triển giao diện người dùng web, đã có từ lâu. JSF 1.2 (kết hợp chặt chẽ trong Java Enterprise Edition 5) đã sửa chữa một số điểm phiền hà của JSF và thêm vào một số tính năng tốt đẹp. Loạt bài hướng dẫn này trình bày cách bắt đầu với JSF 1.2 như thế nào. Nó nặng về các ví dụ và nhẹ về lý thuyết — đúng những gì bạn cần để bắt đầu nhanh chóng.

Trước khi bạn bắt đầu

Giới thiệu về loạt bài này

Loạt bài hướng dẫn này nói về bước khởi đầu với công nghệ JavaServer 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

Hướng dẫn này đi theo một cách tiếp cận cơ bản để phát triển JSF. Bạn sẽ không sử dụng các công cụ màu mè hay sự hỗ trợ IDE trong hướng dẫn này (mặc dù sự hỗ trợ sử dụng công cụ là ích lợi chính của JSF). Bạn sẽ thực hiện lập trình bằng tay không! Tôi đề cập những điểm cốt yếu, với lý thuyết vừa đủ để tiếp tục duy trì việc trình bày và để cho bạn học tập một cách hiệu quả việc sử dụng JSF để xây dựng các ứng dụng Web. Bạn có thể ngạc nhiên khi biết rằng JSF lập trình dễ dàng hơn so với khung công tác Web Java khác, thậm chí không có các công cụ IDE ưa thích.

Các mục tiêu

Trong hướng dẫn này, bạn nhận được một tổng quan về các tính năng của JSF và tìm hiểu cách làm thế nào để viết một ứng dụng JSF cơ bản. Bạn xây dựng một ứng dụng máy tính bỏ túi đơn giản và, tuần tự nhiều lần, cải thiện vẻ ngoài và cảm nhận của nó, sửa đổi cấu trúc của nó để bổ sung thêm nội xạ phụ thuộc và thực hiện cơ chế dẫn hướng của JSF. Trong Phần 2, bạn sẽ xây dựng các trình biến đổi (converter), các bộ duyệt tính hợp lệ, và trình nghe pha (phase-listener) theo yêu cầu.

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 điều kiện cần trước

Hướng dẫn này được viết cho các nhà phát triển Java với 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 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 sẵn. Xem Tải về để nhận được mã ví dụ.


JSF dành cho những người mới bắt đầu

Giống như Swing và AWT, JSF là một khung công tác phát triển cung cấp một bộ các thành phần GUI tiêu chuẩn, có thể dùng lại được, JSF được dùng để xây dựng các giao diện ứng dụng Web. JSF cung cấp các lợi thế phát triển sau:

  • Tách biệt hoàn toàn giữa hành vi và cách trình bày
  • Kiểm soát tính có trạng thái (statefulness) ở mức thành phần
  • Các sự kiện dễ dàng được liên kết với mã phía máy chủ
  • Sử dụng các khái niệm thành phần UI và tầng Web (Web-tier) quen thuộc
  • Cung cấp nhiều dụng cụ của nhà sản xuất phần mềm đã tiêu chuẩn hóa
  • Sự hỗ trợ IDE tuyệt vời

Một ứng dụng JSF điển hình bao gồm các phần sau đây:

  • JavaBeans để quản lý trạng thái và hành vi của ứng dụng
  • Các thành phần GUI có trạng thái
  • Phát triển hướng sự kiện (thông qua các trình nghe-listener) giống như trong phát triển GUI truyền thống
  • Các trang biểu diễn các khung nhìn theo phong cách Model-View-Controller (MVC); các trang web tham khảo các gốc khung nhìn (view roots) thông qua cây thành phần JSF

Bạn sẽ cần phải có khả năng để vượt qua một số các chướng ngại thuộc về khái niệm để sử dụng JSF, nhưng làm như vậy cũng đáng công cố gắng. Việc quản lý trạng thái thành phần của JSF; việc duyệt tính hợp lệ của dữ liệu đầu vào của người dùng dễ sử dụng; xử lý sự kiện mức chi tiết, dựa trên thành phần; và kiến trúc mở rộng dễ dàng; tất cả những cái đó sẽ làm đơn giản đáng kể công sức phát triển web của bạn. Phần này giải thích những tính năng quan trọng nhất trong số này một cách chi tiết.

Một kiến trúc dựa trên-thành phần

Hãy nghĩ theo cách của các thành phần có trạng thái

Chướng ngại lớn nhất mà bạn có thể gặp phải là bạn đã quên rằng JSF là một mô hình thành phần có trạng thái. Nếu bạn đã sử dụng Struts từ trước, hãy lặp lại theo tôi câu này: "JSF không phải là Struts. JSF không phải là Struts". Tôi đã thấy những người có một nền tảng về GUI của Swing, AWT, Visual Basic, hay Delphi học JSF nhanh hơn nhiều so với những người đã sử dụng Struts nhiều năm, và chưa bao giờ thực hiện phát triển thành phần GUI trước đó. Hướng dẫn này được thiết kế để giúp bạn nghĩ theo cách của các thành phần có trạng thái.

JSF cung cấp các thẻ thành phần cho tất cả các trường đầu vào có sẵn trong HTML tiêu chuẩn. Bạn cũng có thể viết các thành phần tuỳ biến riêng của bạn cho các mục đích riêng cho ứng dụng hoặc tổ hợp nhiều thành phần HTML để tạo thành một phức hợp — ví dụ, một thành phần trình chọn ngày tháng (Data Picker) có chứa ba danh sách chọn thả xuống. Các thành phần JSF có trạng thái. Tính có trạng thái của chúng do các khung công tác JSF cung cấp. JSF sử dụng các thành phần để sinh ra đáp ứng HTML. Nhiều thành phần GUI JSF của bên thứ ba cũng có sẵn.

JSF bao gồm:

  • Một mô hình sự kiện-xuất bản
  • Một bộ chứa (container) nhẹ, đảo ngược-điều khiển (IoC)
  • Các thành phần cho hầu hết mọi đặc tính GUI phổ biến khác, bao gồm (nhưng không hạn chế):
    • Công cụ biểu hiện (rendering) cắm chạy được
    • Trình duyệt tính hợp lệ phía máy chủ
    • Biến đổi dữ liệu
    • Quản lý dẫn hướng trang

Là một kiến trúc dựa trên thành phần, JSF có khả năng cấu hình và mở rộng rất cao. Hầu hết các chức năng JSF — như dẫn hướng và tìm kiếm các bean-quản lý — có thể được thay thế bằng các thành phần cắm được. Tính dễ dàng cắm thêm này mang lại cho bạn sự linh hoạt đáng kể trong việc xây dựng các GUI ứng dụng Web của bạn và cho phép bạn kết hợp chặt chẽ các công nghệ dựa trên thành phần khác một cách dễ dàng với các nỗ lực phát triển JSF của bạn. Ví dụ, bạn có thể thay thế khung công tác có sẵn đảo ngược điều khiển (IoC) của JSF bằng khung công tác Spring lập trình hướng khía cạnh (aspect-oriented programming - AOP)/đảo ngược điều khiển đầy đủ tính năng hơn đối với việc tìm kiếm bean quản lý. Tôi sẽ trình bày nhiều về các tính năng nâng cao ấy trong phần 2.

Công nghệ JSF và JSP

Giao diện người dùng của một ứng dụng JSF bao gồm các trang JavaServer Pages (JSP). Mỗi trang JSP có chứa các thành phần JSF để thể hiện chức năng GUI. Bạn sử dụng các thư viện thẻ tùy biến JSF bên trong các trang JSP để biểu hiện các thành phần UI, để đăng ký trình xử lý sự kiện, để kết hợp các thành phần với các trình duyệt tính hợp lệ (validator), để kết hợp các thành phần với các trình biến đổi dữ liệu (converter), và nhiều hơn nữa.

JSF không có kiến thức về JSP

JSF không có gì để làm với bản thân JSP. JSF làm việc với JSP thông qua một cầu nối thư viện thẻ JSP. Tuy nhiên, vòng đời của JSF là rất khác so với vòng đời của JSP. Facelets phù hợp với JSF hơn nhiều so với JSP vì Facelets đã được thiết kế với JSF trong tâm trí, trong khi việc kết hợp JSF và JSP luôn luôn giống như bắt một miếng gỗ hình vuông vào trong một lỗ tròn. Bạn nên suy nghĩ về Facelets; các tính năng của Facelets sẽ là một phần của JSF 2.0. Xem Tài nguyên để biết thêm thông tin về Facelets.

Điều đó nói rằng, sự thật là JSF vốn đã không ràng buộc với công nghệ JSP. Trong thực tế, các thẻ JSF được các trang JSP sử dụng chỉ để tham khảo các thành phần sao cho chúng có thể được hiển thị. Các thành phần có một vòng đời khác với các trang JSP.

Bạn sẽ thấy rõ điều này lần đầu tiên khi bạn sửa đổi một trang JSP để thay đổi các thuộc tính của một thành phần JSF và nạp lại trang... và không có điều gì xảy ra. Đó là do thẻ này tìm kiếm thành phần trong trạng thái hiện tại của nó. Nếu thành phần đã tồn tại, thẻ tuỳ biến không thay đổi trạng thái của nó. Mô hình thành phần cho phép mã của trình điều khiển của bạn thay đổi một trạng thái của thành phần (ví dụ, vô hiệu một trường văn bản), và khi khung nhìn đó được hiển thị, trạng thái hiện tại của cây thành phần của bạn được hiển thị.

Một ứng dụng JSF điển hình không cần phải có mã Java và có rất ít mã ngôn ngữ biểu thức phổ quát (JSTL EL) trong UI. Như tôi đã lưu ý trước đó, có rất nhiều công cụ IDE để xây dựng và hợp dịch các ứng dụng trong JSF, và có một thị trường của bên thứ ba cho các thành phần GUI của JSF. Cũng có khả năng viết mã JSF mà không sử dụng các công cụ WYSIWYG (như bạn sẽ làm trong hướng dẫn này), mặc dù JSF đã được thiết kế với các công cụ WYSIWYG IDE trong tâm trí.

Chúng ta không cần sự hỗ trợ khó chịu của IDE WYSIWYG

Mặc dù JSF đã được thiết kế với sự hỗ trợ IDE WYSIWYG trong tâm trí, bạn không cần phải sử dụng sự hỗ trợ IDE WYSIWYG để có được các lợi ích của JSF. Trong thực tế, JSF vẫn còn dễ sử dụng hơn nhiều hơn so với hầu hết các khung công tác Web Java ngay cả khi bạn viết mã nó bằng tay. Nếu bạn lập trình trong Swing, và bạn sử dụng một IDE WYSIWYG, thì nhiều khả năng bạn sẽ sử dụng một công cụ như vậy với JSF. Nếu bạn thích viết mã Swing bằng tay hơn, bạn cũng sẽ thích mã hóa JSF bằng tay. Đó là lập trình tay không!

Các cải tiến trong JSP 1.2 và JSF 2.1

JSP 2.1 thêm nhiều tính năng mới để hỗ trợ JSF, bao gồm API ngôn ngữ biểu thức phổ quát (EL) mà JSF 1.2 cũng bổ sung thêm vào. Với các thẻ JSTL tiêu chuẩn, bây giờ bạn có thể lặp qua một danh sách và biểu hiện các thành phần JSF, điều mà JSF 1.1 và JSP 2.0 không thể làm. Xem Tài nguyên để biết thêm chi tiết về các thay đổi đã được thực hiện trong JSP 2.1. (Ngay cả với những cải tiến này, Facelets vẫn phù hợp JSF tốt hơn nhiều, và có rất nhiều ý tưởng từ Facelets sắp được đưa vào trong JSF 2.0.)

JSF và MVC

JSF là kết quả của các bài đã học thu được qua nhiều năm phát triển của các kỹ thuật phát triển Web trên nền tảng Java. Xu hướng này bắt đầu với công nghệ JSP, cho dù có nhiều ưu điểm, đã làm cho quá dễ dàng trộn lẫn mã Java với mã HTML (và mã giống như HTML). Bước tiến tiếp theo là kiến trúc Mô hình 1 trong đó các nhà phát triển đẩy hầu hết mã mặt sau vào trong các thành phần JavaBeans và sau đó nhập khẩu các thành phần JavaBeans vào các trang Web với thẻ <jsp:useBean>. Điều này đã hoạt động tốt với các ứng dụng Web đơn giản, nhưng nhiều nhà phát triển Java không thích kết hợp công nghệ JSP với các đặc tính C++ ví dụ như là lệnh bao gồm (include) tĩnh. Vì vậy, kiến trúc Mô hình 2 đã được giới thiệu.

Về cơ bản, kiến trúc Mô hình 2 là phiên bản MVC thấp hơn cho các ứng dụng Web. Trong kiến trúc Mô hình 2, trình điều khiển được thể hiện bằng các servlet (hoặc các Action) và việc hiển thị được giao cho các trang JSP. Struts của Apache là một thực thi Mô hình 2 đơn giản hoá, nơi mà các Action thế chỗ các servlet. Trong Struts, logic của trình điều khiển ứng dụng được tách ra khỏi các dữ liệu của nó (được biểu diễn bằng ActionForms). Các lời phàn nàn chủ yếu chống lại Struts là có thể cảm thấy nó có nhiều tính thủ tục hơn hướng đối tượng ("COBOL cho Web"). WebWork và Spring MVC là hai kiến trúc Mô hình 2 khác để cải tiến Struts để ít tính thủ tục hơn, nhưng cả hai đều không được chấp nhận rộng rãi như là Struts. Và cả hai cũng không cung cấp một mô hình thành phần có trạng thái như JSF đã làm. (Struts 2 được xây dựng bên trên WebWork, và cơ sở mã lệnh Struts ban đầu đã bị loại bỏ. Ngay cả Struts cũng không muốn).

Vấn đề thực sự với hầu hết các khung công tác Mô hình 2 là ở chỗ mô hình sự kiện quá đơn giản (chủ yếu là một MVC thu nhỏ nhiều), và nó không có thành phần GUI có trạng thái, để lại quá nhiều công việc cho nhà phát triển. Một mô hình thành phần và sự kiện phong phú hơn sẽ làm cho dễ dàng tạo ra các loại tương tác mà hầu hết người dùng mong đợi. Cũng giống như công nghệ JSP, hầu hết các khung công tác của Mô hình 2 cũng làm cho quá dễ dàng trộn lẫn bố trí và định dạng HTML với các thẻ tùy biến GUI, hoạt động hơi giống như các thành phần, ngoại trừ là chúng không có trạng thái. Và một số kiến trúc Mô hình 2 (như Struts kinh điển) phạm lỗi tách biệt hành vi với trạng thái, đã mang lại cho nhiều nhà phát triển Java cảm giác như họ đang lập trình COBOL.

Một môi trường MVC phong phú hơn

JSF không phải là Struts; hãy trút hết cốc của bạn để bạn có thể đổ đầy nó

JSF không phải là một khung công tác Mô hình 2. Nó có nhiều hơn thế. Nhiều người tin rằng do tác giả ban đầu của Struts đã là người lãnh đạo đặc tả kỹ thuật JSF, nên các kỹ năng Struts có thể được sử dụng trên một dự án JSF. Đừng cố gắng lập trình JSF giống như Struts. Bạn cần phải từ bỏ một số kỹ năng Struts của bạn và học các kỹ năng JSF.

JSF cung cấp một mô hình thành phần và một môi trường MVC phong phú — phong phú hơn nhiều khung công tác Mô hình 2. JSF gần gũi hơn nhiều với một môi trường lập trình MVC thực thụ hơn là gần kiến trúc Mô hình 2, mặc dù nó vẫn đang được xây dựng bên trên một giao thức không trạng thái. JSF cũng tạo thuận lợi cho việc xây dựng các GUI hướng sự kiện, mức chi tiết mịn hơn so với các khung công tác Mô hình 2. Trong khi JSF cung cấp cho bạn một số đông các tùy chọn sự kiện — mục trình đơn được chọn, nút ấn được nhấn, nút cây được mở rộng,... — hầu hết các Mô hình 2 chỉ dựa trên một sự kiện đơn giản là "đã nhận được yêu cầu."

Mô hình sự kiện tinh chỉnh được của JSF cho phép các ứng dụng của bạn ít bị ràng buộc với các chi tiết HTTP và làm đơn giản nỗ lực phát triển của bạn. JSF cũng cải tiến một chút kiến trúc Mô hình 2 truyền thống bằng cách làm dễ dàng hơn việc di chuyển logic trình bày và logic nghiệp vụ ra ngoài trình điều khiển của bạn, và di chuyển logic nghiệp vụ ra khỏi các trang JSP của bạn. Trong thực tế, các lớp của trình điều khiển đơn giản không bị ràng buộc chút nào với JSF, làm cho chúng dễ dàng thử nghiệm hơn. Không giống như một kiến trúc MVC thực thụ, tầng mô hình JSF ít có khả năng phát ra nhiều sự kiện phải được giải quyết trong nhiều hơn một cổng nhìn (mặc dù Crank cố gắng thực hiện điều này, với sự hỗ trợ từ JBoss ajax4JSF; xem Tài nguyên). Một lần nữa, điều này sẽ là không cần thiết bởi vì bạn đang làm việc với một giao thức phi trạng thái. Sự kiện hệ thống để thay đổi hoặc cập nhật một khung nhìn hầu như luôn luôn (tôi dám nói luôn luôn?) là một yêu cầu từ người sử dụng.

Các chi tiết về thực thi MVC của JSF

Trong thực thi MVC của JSF, việc ánh xạ beans quản lý sẽ làm trung gian giữa khung nhìn và mô hình. Vì thế, điều quan trọng là hạn chế logic nghiệp vụ và logic lâu bền trong các beans quản lý, được gắn với JSF. Một giải pháp thay thế chung là giao logic nghiệp vụ cho mô hình ứng dụng. Trong trường hợp này, các bean quản lý cũng ánh xạ với các đối tượng của mô hình, ở nơi khung nhìn có thể hiển thị chúng (như các thuộc tính của bean quản lý). Tôi có xu hướng tách biệt beans quản lý của tôi ra thành hai loại: beans quản lý được gắn với JSF (trình điều khiển) và beans quản lý không được gắn với JSF (các đối tượng mô hình).

Không giống như công nghệ JSP, thực thi của khung nhìn JSF là một mô hình thành phần có trạng thái. Khung nhìn JSF gồm hai phần gốc khung nhìn (view root) và các trang JSP. Gốc khung nhìn là một sưu tập các thành phần UI để duy trì trạng thái của UI. Giống như Swing và AWT, các thành phần JSF sử dụng một mẫu thiết kế phức hợp để quản lý một cây các thành phần (nói đơn giản: một bộ chứa chứa các thành phần; một bộ chứa là một thành phần). Trang JSP liên kết các thành phần UI với các trang JSP và cho phép bạn liên kết các thành phần trường với các thuộc tính của beans hậu thuẫn (hay đúng hơn là các thuộc tính của thuộc tính) và liên kết các nút nhấn với trình xử lý sự kiện và các phương thức hành động.

Hình 1 minh hoạ cấu trúc của một ví dụ ứng dụng (một ứng dụng mà bạn sắp tìm hiểu chi tiết!) theo một quan điểm của MVC:

Hình 1. Ứng dụng mẫu từ một phối cảnh MVC
Ứng dụng mẫu từ một phối cảnh MVC

Nếu phần đầu tiên này về JSF đã làm bạn phải lắc đầu một chút cũng đừng lo lắng: bạn đã vượt qua lúc khó khăn nhất. Đi sâu vào khung công tác về mặt khái niệm của JSF là đã qua được hơn một nửa cuộc chiến với việc thực hiện công nghệ này — và bạn sẽ sớm thấy rằng nó cũng đáng công khó nhọc. Nhưng lý thuyết thế là đủ: chúng ta hãy tay không đi viết vài đoạn mã!


Một ví dụ JSF: Dần từng bước một

Phần này tập trung vào quá trình từng bước tạo ra một ứng dụng trong JSF. Ví dụ ứng dụng là một phần mềm máy tính bỏ túi (Calculator) đơn giản để giải thích các khía cạnh sau đây của việc sử dụng công nghệ JSF:

  • Làm thế nào để bố trí một ứng dụng JSF để triển khai
  • Làm thế nào để cấu hình một tệp tin web.xml cho JSF
  • Làm thế nào để cấu hình một tệp tin faces-config.xml cho một ứng dụng
  • Viết bean quản lý (còn gọi là các đối tượng mô hình và các trình điều khiển)
  • Xây dựng khung nhìn bằng cách sử dụng công nghệ JSP
  • Sử dụng các thư viện thẻ tùy biến để xây dựng cây thành phần trong gốc khung nhìn
  • Mặc định duyệt tính hợp lệ của các trường biểu mẫu

Trong phần tiếp theo, bạn sẽ cải tiến ứng dụng qua một số lần lặp đi lặp lại và làm cho nó hiểu JSF nhiều hơn. Hình 2 cho thấy một khung nhìn có chú thích của phiên bản cuối cùng của ví dụ ứng dụng máy tính bỏ túi sẽ trông như thế nào. Xem Tải về để lấy mã nguồn ứng dụng.

Hình 2. Ứng dụng Máy tính bỏ túi, bản cuối cùng
Ứng dụng cuối

Ứng dụng Calculator (Máy tính bỏ túi)

Xây dựng bằng Maven 2 và WTP Eclipse

Môi trường xây dựng mặc định cho ví dụ ứng dụng Máy tính bỏ túi là Apache Maven 2. Trong ví dụ này, tôi đã sử dụng một bố trí mặc định cho một ứng dụng Web Maven. Xem Tài nguyên với các chỉ dẫn và các JAR để chạy mã nguồn của hướng dẫn này với Eclipse JEE, Tomcat, và Maven 2.

Mục tiêu của ứng dụng Calculator ban đầu là để trình diễn một trang web cho phép người dùng nhập vào hai số và sau đó cộng hoặc nhân chúng.

Trang này có:

  • Một biểu mẫu
  • Hai trường văn bản
  • Hai nhãn
  • Hai vị trí thông báo lỗi
  • Hai nút Submit
  • Một bảng kết quả

Các trường văn bản dùng để nhập vào các số. Các nhãn dùng để gắn nhãn cho các trường văn bản. Các vị trí thông báo lỗi dùng để hiển thị thông báo về duyệt tính hợp lệ hoặc thông báo lỗi biến đổi dữ liệu cho các trường văn bản. Có hai trang JSP: calculator.jsp và index.jsp, chỉ để chuyển hướng vào calculator.jsp. Một bean quản lý có tên là Calculator dùng như là mô hình cho calculator.jsp. Ví dụ đơn giản này hiện không có tầng trình điều khiển nào khác với những cái do JFS cung cấp.

Tạo ứng dụng: Tổng quan

Để xây dựng ứng dụng Calculator đầu tiên trong JSF bạn cần phải:

  • Khai báo Faces Servlet và thêm ánh xạ Faces Servlet trong tệp tin web.xml của bộ mô tả triển khai ứng dụng Web
  • Chỉ rõ tệp tin faces-config.xml trong tệp tin web.xml
  • Tạo lớp Calculator
  • Khai báo bean Calculator trong tệp tin faces-config.xml file
  • Tạo trang index.jsp
  • Tạo trang calculator.jsp

Ứng dụng sử dụng cách bố trí thư mục sau:

+---src
    +---main
        +---java
        +---webapp
            +---pages
            +---WEB-INF
                +---lib

Mã Java ở trong src/main/java/. Tệp tin web.xml ở trong thư mục src/main/webapp/WEB-INF. Tệp tin cấu hình JSF cũng nằm trong thư mục src/main/webapp/WEB-INF. Ví dụ ứng dụng đã được tạo ra với IDE Eclipse cho các nhà phát triển Java EE (Eclipse JEE), trong đó bao gồm một trình thủ thuật tạo dự án JSF để tạo tệp tin web.xml và tệp tin faces-config.xml có các mục đúng đắn. Tôi giả sử rằng bạn sẽ sử dụng một máy chủ ứng dụng hỗ trợ Java EE 5, có nghĩa là, có các tệp tin JSF và JSTL JAR. Xem Tài nguyên để tìm các chỉ dẫn cho việc thiết lập Tomcat để chạy JSF, thiết lập Eclipse JEE để chạy Tomcat 6, và chạy các ví dụ với Maven 2.

Khai báo servlet Faces và ánh xạ servlet

Để sử dụng Servlet Faces, trước hết bạn cần phải cài đặt nó trong tệp tin web.xml của bạn, như được hiển thị trong Listing 1:

Listing 1. Khai báo Servlet Faces trong web.xml
<servlet>
  <servlet-name>Faces Servlet</servlet-name>
  <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

Điều này tương tự với hầu hết các bộ mô tả web.xml, ngoại trừ rằng bạn đang chuyển điều khiển đến servlet JSF để xử lý các yêu cầu thay vì chỉ rõ servlet của riêng bạn. Tất cả các yêu cầu đến các tệp tin JSP có sử dụng một thẻ <f:view> (được ứng dụng mẫu sử dụng) phải đi qua servlet này. Vì thế, bạn cần phải thêm một ánh xạ và chỉ nạp công nghệ JSP có khả năng JSF thông qua ánh xạ đó, như được hiển thị trong Listing 2:

Listing 2. Ánh xạ đường dẫn servlet Faces trong web.xml
<servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>*.jsf</url-pattern>
</servlet-mapping>

<servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>/faces/*</url-pattern>
</servlet-mapping>

Listing 2 báo cho bộ chứa (container) Servlet Faces gửi tất cả các yêu cầu bắt đầu với /faces/ hoặc kết thúc bằng *.jsf tới servlet Faces để xử lý. Điều này cho phép JSF khởi tạo bối cảnh Faces và gốc khung nhìn trước khi hiển thị trang JSF. Gốc khung nhìn có chứa cây thành phần JSF. Bối cảnh Faces là một cách để tương tác với JSF.

Điều này có nghĩa là để tải ứng dụng Calculator bạn sử dụng http://localhost:8080/calculator/pages/calculator.jsf hoặc http://localhost:8080/calculator/faces/pages/calculator.jsp — không phải http://localhost:8080/calculator/pages/calculator.jsp. Nếu bạn nạp trang JSP bên ngoài bối cảnh JSF, JSF sẽ không có cơ hội để khởi tạo các bối cảnh Faces hoặc gốc khung nhìn.

Chỉ rõ tệp tin faces-config.xml file

Nếu bạn đặt tên tệp tin cấu hình Faces của bạn là faces-config.xml và đặt nó vào trong thư mục WEB-INF của các ứng dụng Web của bạn, thế thì Servlet Faces chọn nó và sử dụng nó tự động (vì đó là mặc định). Một cách khác, bạn có thể nạp một hoặc nhiều tệp tin cấu hình ứng dụng thông qua một tham số khởi tạo — javax.faces.application.CONFIG_FILES — trong tệp tin web.xml của bạn với một danh sách các tệp tin được phân cách bằng dấu phẩy như là phần thân. Nhiều khả năng bạn sẽ sử dụng phương thức tiếp cận thứ hai cho tất cả, trừ các ứng dụng JSF đơn giản nhất. Bởi vì ví dụ ứng dụng là đơn giản, hãy sử dụng vị trí tệp tin faces-config.xml mặc định ở /src/main/webapp/WEB-INF.

Tạo lớp Calculator

Bây giờ bạn sẽ tạo một POJO (plain old Java object - đối tượng thuần Java) được gọi là Calculator không bị trói buộc tý nào với JSF, và sau đó liên kết nó với JSF bằng các liên kết phương thức và các liên kết thuộc tính. Lớp đơn giản này, có hai thuộc tính: firstNumbersecondNumber.

Mục tiêu của tôi là giải thích cách làm thế nào để bắt đầu với JSF, vì vậy tôi vẫn duy trì đối tượng mô hình rất đơn giản. Mô hình của ứng dụng này được chứa bên trong một đối tượng mô hình, như hiển thị trong Listing 3. Sau đó bạn sẽ phân chia nó thành hai lớp: trình điều khiển và mô hình.

Listing 3. POJO Calculator
package com.arcmind.jsfquickstart.model;

/**
 * Calculator. Simple POJO.
 *
 * @author Rick Hightower
 */
public class Calculator {

    /** First number used in operation. */
    private int firstNumber = 0;

    /** Result of operation on first number and second number. */
    private int result = 0;

    /** Second number used in operation. */
    private int secondNumber = 0;

    /** Add the two numbers. */
    public void add() {
    result = firstNumber + secondNumber;
  }

    /** Multiply the two numbers. */
  public void multiply() {
    result = firstNumber * secondNumber;
  }

  /** Clear the results. */
  public void clear() {
    result = 0;
  }

  /* ---------- properties ------------- */

  public int getFirstNumber() {
    return firstNumber;
  }

  public void setFirstNumber(int firstNumber) {
    this.firstNumber = firstNumber;
  }

  public int getResult() {
    return result;
  }

  public void setResult(int result) {
    this.result = result;
  }

  public int getSecondNumber() {
    return secondNumber;
  }

  public void setSecondNumber(int secondNumber) {
    this.secondNumber = secondNumber;
  }

}

Listing 3 là đơn giản và không cần giải thích; chỉ cần đọc mã. Tuy nhiên hãy nhớ rằng POJO Calculator không có gì để làm với JSF.

Khai báo bean Calculator trong tệp tin faces-config.xml file

Listing 4 hiển thị toàn bộ tệp tin faces-config.xml. Như bạn có thể nhìn thấy, một phần lớn của nó chỉ kết hợp tệp tin này với lược đồ Java EE JSF XML. Trong faces-config.xml, phần tử <managed-bean> được sử dụng để khai báo một bean mà JSF có thể liên kết tới:

Listing 4. Tệp tin faces-config.xml có chứa khai báo bean quản lý
<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
     http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
     version="1.2">
  <managed-bean>
    <managed-bean-name>calculator</managed-bean-name>
    <managed-bean-class>com.arcmind.jsfquickstart.model.Calculator</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>

</faces-config>

Khai báo bean trong Listing 4 chỉ rõ tên của bean là — calculator — trong <managed-bean-name>. Nó chỉ rõ tên lớp phân biệt đầy đủ trong <managed-bean-class>. Lớp phải có một hàm tạo (constructor) không có đối số.

<managed-bean-scope> của phần tử <managed-bean> chỉ rõ nơi JSF có thể tìm thấy bean: đó là phạm vi yêu cầu. Điều này được thực hiện thông qua JSF và API EL phổ quát. Phạm vi yêu cầu chỉ sẵn sàng cho một yêu cầu mà thôi. Đó là một vị trí tốt để đặt bean mà không cần phải duy trì trạng thái giữa các khung nhìn trang.

Tạo trang index.jsp

Mục đích của trang index.jsp trong ứng dụng Calculator là để đảm bảo rằng trang calculator.jsp nạp vào trong bối cảnh JSF sao cho có thể tìm thấy gốc khung nhìn tương ứng. Listing 5 hiển thị trang index.jsp:

Listing 5. Trang index, chuyển hướng đến calculator.jsp
<jsp:forward page="/faces/calculator.jsp" />

Toàn bộ công việc mà trang này làm là chuyển hướng người dùng tới calculator.jsp trong bối cảnh Web faces. Điều này đặt trang calculator.jsp trong đường dẫn bối cảnh JSF, nơi nó có thể tìm thấy gốc khung nhìn của nó.

Tạo trang calculator.jsp

Trang calculator.jsp là trung tâm của khung nhìn của ứng dụng Calculator. Trang này sẽ nhận hai số do người dùng nhập vào, như được hiển thị trong Hình 3:

Hình 3. Ứng dụng Calculator đầu tiên đang chạy trong Eclipse JEE/WTP
Ứng dụng Calculator đầu tiên

Toàn bộ mã lệnh cho trang này nằm trong Listing 6:

Listing 6. /src/main/webapp/calculator.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">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Calculator Application</title>
</head>
<body>
<f:view>
  <h:form id="calcForm">
    <h4>Calculator</h4>
    <table>
      <tr>
        <td><h:outputLabel value="First Number" for="firstNumber" /></td>
        <td><h:inputText id="firstNumber"
          value="#{calculator.firstNumber}" required="true" /></td>
        <td><h:message for="firstNumber" /></td>
      </tr>

      <tr>
        <td><h:outputLabel value="Second Number" for="secondNumber" />
        </td>
        <td><h:inputText id="secondNumber"
          value="#{calculator.secondNumber}" required="true" /></td>
        <td><h:message for="secondNumber" /></td>
      </tr>
    </table>
    <div>

      <h:commandButton action="#{calculator.add}"  value="Add" />
      <h:commandButton action="#{calculator.multiply}"  value="Multiply" />
      <h:commandButton action="#{calculator.clear}"  value="Clear" immediate="true"/>
    </div>

  </h:form>

  <h:panelGroup rendered="#{calculator.result != 0}">
    <h4>Results</h4>
    <table>
      <tr><td>
        First Number  ${calculator.firstNumber}
      </td></tr>
      <tr><td>
        Second Number ${calculator.secondNumber}
      </td></tr>
      <tr><td>
        Result ${calculator.result}
      </td></tr>
    </table>
  </h:panelGroup>
</f:view>

</body>
</html>

Lưu ý rằng hầu hết tệp tin này là HTML thuần tuý (chính xác là XHTML). Bạn có thể sử dụng HTML bên trong các thẻ <f:view>, <h:form>, và <h:panelGroup>. Thật là hoang đường khi tin rằng bạn không thể kết hợp HTML với các thẻ JSF. Bạn có thể kết hợp trong nhiều trường hợp. Bạn không thể sử dụng HTML bên trong <h:commandButton>, Nó chỉ nhận các thành phần khác như là phần tử con bên trong thân của nó.

Bởi vì trang này khá phức tạp, tôi sẽ chỉ cho bạn cách làm thế nào để từng bước xây dựng nó.

Khai báo thư viện thẻ (taglib)

Bạn hãy bắt đầu bằng cách khai báo các thẻ cho JSF, như hiển thị trong Listing 7:

Listing 7: Nhập các thư viện thẻ vào trong calculator.jsp
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

Listing 7 báo cho máy JSP biết rằng bạn muốn sử dụng cả hai thư viện thẻ JSF là htmllõi (core). Thư viện thẻ html chứa tất cả các thẻ để làm việc với các biểu mẫu và các mục HTML đặc thù khác. Thư viện thẻ lõi có chứa tất cả các thẻ logic, duyệt tính hợp lệ, trình điều khiển, và các thẻ đặc thù cho JSF.

Thẻ <f:view>

Sau khi bạn đã trình bày một trang dưới dạng HTML bình thường, bạn muốn thông báo cho hệ thống JSF rằng bạn sắp sử dụng JSF để quản lý các thành phần của bạn. Bạn làm điều này bằng cách sử dụng thẻ <f:view> để thông báo cho bộ chứa rằng bạn đang sử dụng JSF để quản lý các thành phần bên trong nó.

Nếu không có <f:view>, JSF không thể xây dựng cây thành phần và sau đó không thể tìm kiếm cây thành phần đã tạo ra. Sử dụng thẻ <f:view> như được hiển thị trong Listing 8:

Listing 8. Thẻ <f:view> của calculator.jsp
<f:view>
  <h:form id="calcForm">
     ...
  </h:form>
</f:view>

Dòng đầu tiên trong Listing 8 là khai báo về <f:view>, nó nói với bộ chứa rằng nó được JSF quản lý.

Bên trong <f:view>: Thẻ <h:form>

Dòng thứ hai trong Listing 8 là thẻ <h:form> để nói cho JSF biết rằng bạn muốn có một biểu mẫu HTML ở đây. Trong giai đoạn hoàn trả, các thành phần nằm trong thành phần biểu mẫu được tìm kiếm và được yêu cầu tự hoàn trả và thế là chúng tạo ra HTML tiêu chuẩn ở đầu ra. Bạn có thể bố trí các thành phần biểu mẫu theo bất cứ cách nào bạn muốn. Listing 9 là cách bố trí cho trường đầu vào của ứng dụng Calculator:

Listing 9. Bên trong thẻ <h:form> của calculator.jsp: Các trường đầu vào
<table>
  <tr>
    <td><h:outputLabel value="First Number" for="firstNumber" /></td>
    <td><h:inputText id="firstNumber"
      value="#{calculator.firstNumber}" required="true" /></td>
    <td><h:message for="firstNumber" /></td>
  </tr>

  <tr>
    <td><h:outputLabel value="Second Number" for="secondNumber" />
    </td>
    <td><h:inputText id="secondNumber"
      value="#{calculator.secondNumber}" required="true" /></td>
    <td><h:message for="secondNumber" /></td>
  </tr>
</table>

Một lần nữa lưu ý việc sử dụng HTML rất nhiều. Bạn có thể trình bày ứng dụng của bạn với các khoảng rộng, các vạch chia, các bảng, hoặc bất cứ những gì khác mà bạn muốn. JSF là thân thiện với người thiết kế. Thậm chí có nhiều công cụ để cho phép bạn sử dụng JSF trong Dreamweaver (xem Tài nguyên). JSF thậm chí còn thân thiện người thiết kế hơn nhiều, khi bạn sử dụng nó với Facelets (mà bạn có thể sử dụng với JSF phiên bản 1.1 và cao hơn và nó sẽ là một phần của JSF 2.0).

Trong Listing 9 lưu ý rằng cả hai trường inputText sử dụng một biểu thức có giá trị kiểu JSF EL (JavaServer Faces Expression Language – ngôn ngữ biểu thức JSF) làm giá trị cho thuộc tính value (ví dụ, value="#{calculator.firstNumber}"). Lúc đầu, điều này trông rất giống JSTL EL. Tuy nhiên, trên thực tế mã EL phổ quát kết hợp các trường với các giá trị tương ứng của các thuộc tính của bean phía sau. Sự kết hợp này là phản xạ (hai chiều); có nghĩa là, nếu firstNumber đã là 100, thì sẽ hiện lên 100 khi biểu mẫu được hiển thị. Tương tự, nếu người dùng đã đệ trình một giá trị hợp lệ như là 200, thì 200 sẽ là một giá trị mới của đặc tính firstNumber (giả thiết đã qua phép biến đổi và phép duyệt tính hợp lệ, mà bạn sẽ tìm hiểu thêm về sau).

Ngoài các trường này, calcForm được kết hợp với hai hành động bằng cách sử dụng ba commandButtons, như được hiển thị trong Listing 10:

Listing 10. Bên trong thẻ <h:form> của calculator.jsp: Các nút nhấn
<div>
  <h:commandButton action="#{calculator.add}"  value="Add" />
  <h:commandButton action="#{calculator.multiply}"  value="Multiply" />
  <h:commandButton action="#{calculator.clear}"  value="Clear" immediate="true"/>
</div>

Về các phiếu định kiểu (stylesheets)

Vẻ ngoài và cảm nhận của tất cả các thành phần JSF được khai báo thông qua các lớp phiếu định kiểu. Mỗi thành phần có một thuộc tính kiểu dáng (style) để kết hợp với các định nghĩa kiểu dáng nội dòng, và một styleClass để kết hợp thành phần này với một lớp styleSheet. <panelGrid>, mà bạn sẽ sử dụng trong phần kế tiếp của hướng dẫn này, cũng có các thuộc tính kiểu dáng để kết hợp các kiểu dáng cho các hàng và các cột. Trong phần đó, bạn sẽ sử dụng Cascading Style Sheets (CSS) với JSF.

Trong Listing 10 mã lệnh liên kết ba nút nhấn tới các phương thức add(), multiply(), và clear() của lớp calculator , sao cho khi nhấn vào một nút, sẽ gọi ra phương thức tương ứng của nó (giả định rằng việc duyệt tính hợp lệ và biến đổi được thực hiện thành công).

Theo mặc định, JSF duyệt tính hợp lệ của biểu mẫu trước khi thực hiện bất kỳ một phương thức nào (như add() hoặc multiply()). Tuy nhiên, nếu bạn sử dụng thuộc tính immediate="true", như Listing 10 thực hiện cho nút Clear, thì JSF bỏ qua pha duyệt tính hợp lệ và thực hiện ngay phương thức đó mà không cần duyệt tính hợp lệ của biểu mẫu (ít nhiều là như thế — chi tiết hơn sẽ trình bày sau).

Xem kết quả: Thẻ <h:panelGroup>

Cuối cùng, trong thẻ <f:view> bạn hiển thị các kết quả của các phép tính cộng và nhân với một <h:panelGroup>, như trong Listing 11:

Listing 11. Hiển thị kết quả
<h:panelGroup rendered="#{calculator.result != 0}">
  <h4>Results</h4>
  <table>
    <tr><td>
      First Number  ${calculator.firstNumber}
    </td></tr>
    <tr><td>
      Second Number  ${calculator.secondNumber}
    </td></tr>
    <tr><td>
      Result  ${calculator.result}
    </td></tr>
  </table>
</h:panelGroup>

Một lần nữa bạn sử dụng hầu hết là HTML. Lưu ý rằng bạn có thể trộn lẫn các biểu thức kiểu dáng của JSP trong phần thân <h:panelGroup> của bạn. <h:panelGroup> có một biểu thức được hoàn trả, như mọi thành phần JSF đều có. Do đó, phần kết quả chỉ xuất hiện nếu biểu thức calculator.result != 0 là đúng. Vì lý do này, khi những người sử dụng lần đầu tiên nạp trang calculator.jsp, phần này sẽ không hiển thị. Khi họ nhập vào các giá trị, phần kết quả sẽ hiển thị (miễn là các kết quả khác không). (Vấn đề với biểu thức này là ở chỗ nó đặt logic trong khung nhìn. Và, điều gì sẽ xảy ra nếu bạn muốn người sử dụng có khả năng nhập vào 0 + 0 (hay 7 * 0) và hiển thị kết quả? Bạn sẽ sửa lỗi này ở phần sau trong hướng dẫn này.)

Chạy ứng dụng

Struts có dễ hơn JSF không?

Tôi đánh giá rằng cần phải nỗ lực ít nhất gấp đôi để tạo ra một phiên bản Struts kinh điển đơn giản cho ứng dụng JSF đơn giản mà bạn xây dựng ở đây. Khi sử dụng Struts, bạn sẽ cần có hai lớp hành động cho hai nút, mỗi cái yêu cầu một tập các ánh xạ hành động riêng của mình. Bạn cũng cần phải có một ánh xạ hành động để nạp trang đầu tiên, ít nhất giả định rằng bạn đã làm theo khuyến nghị Mô hình 2. Để bắt chước việc duyệt tính hợp lệ mặc định và việc xử lý lỗi mặc định của JSF bạn phải đặt cấu hình Struts để sử dụng khung công tác của trình duyệt tính hợp lệ (validator) hoặc thực hiện công việc tương đương trong phương thức duyệt tính hợp lệ của một ActionForm. Bạn cũng cần phải khai báo một DynaValidatorForm trong cấu hình Struts, hoặc tạo một ActionForm và viết đè lên phương thức duyệt tính hợp lệ của nó, hoặc sử dụng các lớp con (subclass) của ValidatorForm với các móc nối vào khung công tác của trình duyệt tính hợp lệ. Và cuối cùng, có lẽ bạn sẽ cần phải cấu hình một vài chuyển tiếp (có thể là hai tập hợp cho mỗi hành động) hoặc chuyển tiếp toàn cục để cho tất cả các hành động sử dụng. Khai báo “lớp bạn bè” không cho phép các “bạn bè” sử dụng Struts kinh điển trên các ứng dụng mới.

Để chạy ứng dụng này, hãy vào trang web nơi tệp tin WAR được ánh xạ (trên máy tính của tôi đó là http://localhost:8080/calculator0/). Việc này làm cho tệp tin index.jsp nạp trang calculator.jsp dưới bối cảnh JSF. Nếu bạn đang sử dụng WTP Eclipse và bạn đã thiết lập máy chủ, bạn nhấn chuột phải vào trang calculator.jsp trong Navigator và chọn tùy chọn Run As > Run On Server.

Nếu bạn nhập một số dữ liệu văn bản không hợp lệ (ví dụ, abc) hoặc vào trong trường First Number hoặc vào trong trường Second Number và gửi đi, bạn sẽ được đưa quay lại khung nhìn /calculator.jsp, và một thông báo lỗi được hiển thị bên cạnh trường tương ứng. Nếu bạn để lại một trong hai trường rỗng và gửi đi, bạn cũng sẽ được đưa quay lại khung nhìn /calculator.js, và một thông báo lỗi sẽ được hiển thị bên cạnh trường tương ứng. Vì vậy, bạn có thể thấy rằng một số việc duyệt tính hợp lệ gần như là chạy tự động trong JSF chỉ cần chỉ rõ các trường là phải được điền vào và liên kết các trường này với thuộc tính int. Hình 4 chỉ ra cách ứng dụng đối phó với các lỗi duyệt tính hợp lệ và biến đổi dữ liệu như thế nào:

Hình 4. Các lỗi duyệt tính hợp lệ và lỗi biến đổi dữ liệu
Các lỗi duyệt tính hợp lệ và lỗi biến đổi dữ liệu

Lưu ý các thông báo lỗi khó hiểu. Các thông báo lỗi mặc định đối với biến đổi dữ liệu và các giá trị phải điền không thân thiện với người dùng lắm:

  • calcForm:firstNumber: 'abc' phải là một con số nằm giữa -2147483648 và 2147483647. Ví dụ: 9346.
  • calcForm:secondNumber: Lỗi duyệt tính hợp lệ: giá trị phải được điền vào.

Bạn sẽ sửa chữa các lỗi này trong phần tiếp sau.

Sau khi bạn nhập vào và đệ trình hai giá trị có tổng hoặc tích của chúng khác không, phần kết quả xuất hiện, như được hiển thị trong Hình 5:

Hình 5. Bảng kết quả
Bảng kết quả

Cải thiện ví dụ Calculator

Trong phần này, bạn sẽ cải thiện hình thức của ứng dụng Calculator và làm đơn giản nó bằng cách sử dụng các kỹ thuật JSF. Bạn sẽ tìm hiểu cách làm thế nào để sử dụng CSS, thiết lập các thông báo quốc tế hóa (I18N), và cải thiện vẻ ngoài và cảm nhận của ứng dụng theo nhiều cách khác. Bạn cũng cải thiện thông báo lỗi mặc định, vì còn nhiều điều phải sửa.

Sử dụng một lưới kẻ ô các bảng

Trong các phần trước, bạn đã sử dụng HTML rất nhiều để tiến hành việc bố trí. Với HTML, bạn có thể bố trí trang web chính xác theo cách bạn muốn. Tuy nhiên, việc bố trí ứng dụng web có thể không quan trọng bằng, ví dụ, việc bố trí của một tờ gấp tiếp thị. Để làm đơn giản GUI của ứng dụng Calculator kha khá, ngay bây giờ bạn sẽ sử dụng một <h:panelGrid> để bố trí các mục vào trong một lưới kẻ ô.

Listing 12 cho thấy mã lệnh của biểu mẫu đầu vào thay đổi như thế nào để sử dụng <h:panelGrid>:

Listing 12. Thay đổi biểu mẫu để sử dụng <h:panelGrid>
<h:form id="calcForm">
  <h4>Calculator</h4>
    <h:panelGrid columns="3">
      <%-- First Number--%>
      <h:outputLabel value="First Number" for="firstNumber" />
      <h:inputText id="firstNumber"
        value="#{calculator.firstNumber}" required="true" />
      <h:message for="firstNumber" />
      <%-- Second Number--%>
      <h:outputLabel value="Second Number" for="secondNumber" />
      <h:inputText id="secondNumber"
        value="#{calculator.secondNumber}" required="true" />
      <h:message for="secondNumber" />
   </h:panelGrid>

Listing 13 cho thấy phần các kết quả thay đổi như thế nào để sử dụng <h:panelGrid>:

Listing 13. Thay đổi phần các kết quả để sử dụng <h:panelGrid>
<h:panelGroup rendered="#{calculator.result != 0}">
  <h4>Results</h4>
  <h:panelGrid columns="1">
      <h:outputText value="First Number  #{calculator.firstNumber}"/>
      <h:outputText value="Second Number #{calculator.secondNumber}"/>
      <h:outputText value="Result  #{calculator.result}"/>
  </h:panelGrid>
</h:panelGroup>

Bạn đã loại bỏ gần 20 dòng mã (khoảng một phần ba), làm cho nó dễ đọc hơn một chút. Và giờ đây, ứng dụng này có một mức độ "JSFness" cao hơn, mà một số người (ví dụ, hầu hết các nhà phát triển) thích còn những người khác (các nhà thiết kế Web) thì ghét. Bạn cần phải cân đối những gì bạn làm theo yêu cầu của dự án của bạn. Các dự án là thay đổi, và vì thế thực hiện yêu cầu đối với việc kiểm soát tuyệt đối (bố trí chỉ dùng HTML) và các ứng dụng dễ bảo trì (xem thêm JSFness).

Đây là nơi có một số việc cần phải xoay sở khéo léo hơn một chút. Một <h:panelGrid> chỉ có thể chứa các thành phần, trong khi <h:form>, <f:view>, và <h:panelGroup> có thể chứa cả HTML và cả các thành phần <h:panelGrid> đầu tiên trong biểu mẫu có ba cột, do đó, ngay khi bạn thêm một thành phần nữa, nó sẽ chuyển xuống hàng dưới. <h:panelGrid> thứ hai chỉ chứa một cột, do đó, mỗi thành phần sẽ được thêm vào hàng tiếp theo. <h:panelGrid> hoàn trả một bảng, vì thế kết quả gần như giống hệt với những gì bạn đã có trước đó. (Với người sử dụng nó là giống nhau). Nhắc lại: bạn không thể thêm HTML vào một <h:panelGrid>. Nó sẽ không hoàn trả ở nơi mà bạn mong đợi. Nó chỉ có các thành phần.

Che dấu cho GUI bằng CSS

Nếu bạn hiểu biết HTML và CSS, thì bạn biết làm thế nào để tăng thêm vẻ ngoài và cảm nhận của phiên bản đầu tiên của ứng dụng Calculator. <h:panelGrid> cũng cho phép bạn thực hiện điều này. Bạn sẽ nhập khẩu một phiếu định kiểu CSS và sau đó sử dụng nó với <h:panelGrid>. Bạn sẽ làm cho <h:panelGrid> hiện lên với một đường viền và xen kẽ các hàng màu bạc và màu trắng.

Trước tiên, nhập khẩu phiếu định kiểu, như chỉ ra trong Listing 14:

Listing 14. Nhập khẩu phiếu định kiểu
<head>
<title>Calculator Application</title>
  <link rel="stylesheet" type="text/css" 
       href="<%=request.getContextPath()%>/css/main.css" />
</head>

Listing 15 là phiếu định kiểu này:

Listing 15. Phiếu định kiểu CSS
oddRow {
  background-color: white;
}

evenRow {
  background-color: silver;
}

formGrid {
  border: solid #000 3px;
  width: 400px;
}

resultGrid {
  border: solid #000 1px;
  width: 200px;
}

Listing 15 định nghĩa các kiểu dáng (style) của oddRowevenRow, làm cho các hàng lẻ có màu trắng và các hàng chẵn có màu bạc.

Bây giờ áp dụng các kiểu dáng này cho panelGrids. Listing 16 bổ sung chúng vào biểu mẫu panelGrid:

Listing 16. Sử dụng các lớp kiểu dáng với biểu mẫu panelGrid
<h:panelGrid columns="3" rowClasses="oddRow, evenRow"
      styleClass="formGrid">
      ...
</h:panelGrid>

Bằng cách đặt thuộc tính rowClasses="oddRow, evenRow", Listing 16 áp dụng các kiểu dáng oddRowevenRow cho biểu mẫu. styleClass="formGrid" vẽ đường viền xung quanh bảng. <h:panelGrid> của kết quả là tương tự như được hiển thị trong Listing 17 với một đường viền nhỏ hơn:

Listing 17. Sử dụng các lớp kiểu dáng cho panelGrid các kết quả panelGrid
<h:panelGrid columns="1" rowClasses="oddRow, evenRow"
    styleClass="resultGrid">
    ...
</h:panelGrid>

Hình 6 cho thấy ứng dụng Calculator bây giờ trông như thế nào:

Hình 6. Calculator với một số kiểu dáng
Calculator với một số kiểu dáng

Tôi mới chỉ xới trên bề mặt khi nói về các kiểu dáng mà <panelGrid> hỗ trợ. Tham khảo các liên kết đến API thư viện thẻ trong Tài nguyên để biết thêm chi tiết.

Bắt đầu làm sạch các thông báo lỗi

Các thông báo lỗi rất thú vị nếu người dùng của bạn là những người đam mê hoặc nhưng người say mê kỹ thuật. Trái lại, chúng không thân thiện với người dùng. Bạn có thể cải thiện chúng theo nhiều cách khác nhau. Bạn có thể bắt đầu bằng cách thêm vào một nhãn, như được hiển thị trong Listing 18:

Listing 18. Thêm một nhãn
<h:inputText id="firstNumber" label="First Number" ... />
...
<h:inputText id="secondNumber" label="Second Number" .../>
...

Lưu ý rằng bạn sử dụng thuộc tính label="First Number" trong trường h:inputText. Bây giờ bạn có thể nhìn thấy các văn bản thông báo trong Hình7:

Hình 7. Các thông báo có các nhãn
Các thông báo có các nhãn

Các tên nhãn không còn là tên thuộc tính nữa, do đó chúng thân thiện với người dùng hơn. Nhưng rốt cục tại sao bạn muốn các nhãn, vì dù sao các thông báo lỗi được bố trí ngay ở cạnh các trường? Ngoài ra, các thông báo nhãn rất dài. Bạn có thể rút ngắn chúng bằng cách sử dụng mã trong Listing 19:

Listing 19. Hiển thị thông báo tóm tắt thay cho thông báo chi tiết
<h:outputLabel value="First Number" for="firstNumber" />
<h:inputText id="firstNumber" label="First Number"
  value="#{calculator.firstNumber}" required="true" />
<h:message for="firstNumber" showSummary="true" showDetail="false"/>

Lưu ý rằng Listing 19 đặt giá trị cho showSummaryshowDetail của thành phần h:messageshowSummary="true" showDetail="false". Điều này dẫn đến kết quả thông báo là "First Number: 'aaa' phải là một số bao gồm một hoặc nhiều chữ số." và "Second Number: Lỗi duyệt tính hợp lệ: giá trị cần phải điền." tương ứng cho phép biến đổi dữ liệu và yêu cầu phải nhập vào firstNumbersecondNumber. Như thế vẫn chưa tốt. Hãy đọc tiếp để có cách thay thế khác, tốt hơn.

Viết đè lên nội dung thông báo

JSF 1.2 đã thêm một requiredMessage và một conversionMessage, do đó bạn có thể viết đè một thông báo trên cơ sở xét từng trường hợp, như được hiển thị trong Listing 20:

Listing 20. Sử dụng requiredMessageconverterMessge để rút ngắn các thông báo
<%-- First Number--%>
<h:outputLabel value="First Number" for="firstNumber" />
<h:inputText id="firstNumber" label="First Number"
  value="#{calculator.firstNumber}" required="true"
  requiredMessage="required" converterMessage="not a valid number"
  />
<h:message for="firstNumber" />
<%-- Second Number--%>
<h:outputLabel value="Second Number" for="secondNumber" />
<h:inputText id="secondNumber" label="Second Number"
  value="#{calculator.secondNumber}" required="true"
  requiredMessage="required" converterMessage="not a valid number"
  />
<h:message for="secondNumber" />

Lưu ý rằng các h:inputText trong Listing 20 bổ sung thêm requiredMessage="required"(cần phải điền) converterMessage="not a valid number" (không phải là một số hợp lệ). Bây giờ điều này trông hấp dẫn hơn, và các thông báo có ý nghĩa hơn trong bối cảnh của <h:panelGrid>: chúng được bố trí gần các trường, do đó, người sử dụng nhờ bối cảnh mà biết được chúng áp dụng vào đâu (xem Hình 8):

Hình 8. Các thông báo ngắn hơn
Các thông báo ngắn

Một vấn đề với cách tiếp cận này là bạn cần phải thêm requiredMessageconverterMessage tới mọi trường inputText . Đối với ví dụ đơn giản này, điều đó không là vấn đề. Tuy nhiên, đối với một ứng dụng thực sự, đây có thể là một vấn đề bảo trì lớn và chắc chắn phá vỡ nguyên tắc không lặp lại chính mình (DRY).

Thay đổi các thông báo toàn cục

Để thay đổi các thông báo một cách toàn cục, bạn có thể định nghĩa một gói tài nguyên trong tệp tin faces-config.xml và sử dụng nó để định nghĩa lại thông báo mặc định, như được hiển thị trong Listing 21:

Listing 21. Đặt cấu hình thông báo trong faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2">

    <application>
        <message-bundle>messages</message-bundle>
    </application>
  ...

Tệp tin message.properties có các mục được hiển thị trong Listing 22:

Listing 22. Gói tài nguyên các thông báo (messages.properties)
javax.faces.component.UIInput.REQUIRED_detail=required
javax.faces.converter.IntegerConverter.INTEGER_detail=not a valid number

Bây giờ các thông báo đã được thay đổi toàn cục bất cứ khi nào có một trường cần phải điền hay một phép biến đổi số nguyên thất bại.

Chú ý: Nếu bạn đang sử dụng Eclipse JEE, hãy chắc chắn bổ sung thêm thư mục src/main/resources/messages.properties làm một thư mục nguồn.

Những cải tiến mà bạn đã thực hiện trong phần này đã làm tăng tính logic GUI của ứng dụng, do đó trong phần kế tiếp, bạn sẽ thêm một lớp CalculatorController để nội xạ vào lớp Calculator.


Bổ sung thêm một trình điều khiển

Bây giờ bạn sẽ cấu trúc lại mã nguồn ứng dụng, do đó bạn đừng liên kết đối tượng Calculator Java thuần tới JSF và làm hư hỏng tính POJO của nó. Bạn sẽ làm điều này bằng cách tạo một lớp trình điều khiển và nội xạ đối tượng mô hình thuần túy vào trong lớp trình điều khiển. Lớp trình điều khiển JSF sẽ nhận biết JSF, còn lớp mô hình thì không.

Phần này trình bày:

  • Sử dụng bộ chứa nội xạ-phụ thuộc của JSF
  • Làm việc với facesContext của JSF
  • Bổ sung thêm các FacesMessage
  • Sử dụng h:messages
  • Liên kết các thành phần vào trình điều khiển

Bạn sẽ thực hiện qua từng bước này. Sau đó, tôi sẽ quay trở lại và giải thích từng bước chi tiết hơn.

Thêm một phương thức divide() vào Calculator

Trước tiên, thêm một phương thức divide() vào Calculator, như được hiển thị trong Listing 23, để cho bạn có thể sửa lại các lỗi ngoại lệ chia-cho-không và thêm một FacesMessage để hiển thị cho người sử dụng:

Listing 23. Thêm phương thức divide() vào POJO Calculator
package com.arcmind.jsfquickstart.model;

/**
 * Calculator. Simple POJO.
 *
 * @author Rick Hightower
 */
public class Calculator {

    /** First number used in operation. */
    private int firstNumber = 0;

    /** Result of operation on first number and second number. */
    private int result = 0;

  ...

    /** Divide the two numbers. */
    public void divide() {
       this.result = this.firstNumber / this.secondNumber;
    }

    /** Clear the results. */
    public void clear () {
    result = 0;
    }

  ...

}

Tạo lớp trình điều khiển

Tiếp theo, thêm một lớp mới gọi là CalculatorController để điều khiển POJO Calculator.

CalculatorController có ba thành phần JSF được liên kết vào nó. Nó nhận biết JSF rất cao. Nó cũng sửa lại các lỗi ngoại lệ bằng cách đặt FacesMessages trong FacesContext.

Ba thành phần JSF liên kết đến CalculatorController là:

  • resultsPanel, đó là một UIPanel
  • firstNumberInput, đó là một UIInput
  • secondNumberInput, đó là một UInput

Hình 9 cho thấy ứng dụng calculator xử lý các lỗi như thế nào:

Hình 9. Trường hợp ngoại lệ chia-cho-không
Ví dụ ứng dụng

Hình 10 cho thấy trạng thái các báo cáo của ứng dụng như thế nào:

Hình 10. Các kết quả hiển thị với thông báo trạng thái
Các kết quả hiển thị với thông báo trạng thái

CalculatorController, như được hiển thị trong Listing 24, tách món bơ lạc JSF ra khỏi món sô-cô-la POJO của bạn. Nó nhận biết về JSF, có các thành phần liên kết đến nó, và đặt các thông báo lỗi và thông báo trạng thái vào facesContext.

Listing 24. CalculatorController nhận biết JSF
package com.arcmind.jsfquickstart.controller;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIInput;
import javax.faces.component.UIPanel;
import javax.faces.context.FacesContext;
import com.arcmind.jsfquickstart.model.Calculator;

public class CalculatorController {

  private Calculator calculator;
  private UIPanel resultsPanel;  
  private UIInput firstNumberInput;
  private UIInput secondNumberInput;

  public String add() {
    FacesContext facesContext = FacesContext.getCurrentInstance();

    try {
      calculator.add();
      resultsPanel.setRendered(true);
      facesContext.addMessage(null, new FacesMessage(
          FacesMessage.SEVERITY_INFO, "Added successfully", null));

    } catch (Exception ex) {
      resultsPanel.setRendered(false);
      facesContext.addMessage(null, 
          new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
    }
    return null;
  }

  public String multiply() {
    FacesContext facesContext = FacesContext.getCurrentInstance();

    try {
      calculator.multiply();
      resultsPanel.setRendered(true);
      facesContext.addMessage(null, new FacesMessage(
          FacesMessage.SEVERITY_INFO, "Multiplied successfully", null));

    } catch (Exception ex) {
      resultsPanel.setRendered(false);
      facesContext.addMessage(null, 
          new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
    }
    return null;
  }

  public String divide() {
    FacesContext facesContext = FacesContext.getCurrentInstance();

    try {
      calculator.divide();
      resultsPanel.setRendered(true);
      facesContext.addMessage(null, new FacesMessage(
          FacesMessage.SEVERITY_INFO, "Divided successfully", null));

    } catch (Exception ex) {
      resultsPanel.setRendered(false);
      if (ex instanceof ArithmeticException) {
        secondNumberInput.setValue(Integer.valueOf(1));
      }
      facesContext.addMessage(null, 
          new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
    }
    return null;
  }

  public String clear() {
    FacesContext facesContext = FacesContext.getCurrentInstance();

    try {
      calculator.clear();
      resultsPanel.setRendered(false);
      facesContext.addMessage(null, new FacesMessage(
          FacesMessage.SEVERITY_INFO, "Results cleared", null));

    } catch (Exception ex) {
      resultsPanel.setRendered(false);      
      facesContext.addMessage(null, 
          new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
    }
    return null;
  }

  public String getFirstNumberStyleClass() {
    if (firstNumberInput.isValid()) {
      return "labelClass";
    } else {
      return "errorClass";
    }    
  }
  //remove simple props

Cập nhật calculator.jsp

Tiếp theo, cập nhật calculator.jsp như chỉ ra trong Listing 25 để hiển thị thông báo lỗi và liên kết đến calculatorController thay vì trực tiếp đến POJO Calculator:

Listing 25. calculator.jsp được cập nhật
<?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">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Calculator Application</title>
     <link rel="stylesheet" type="text/css"
          href="<%=request.getContextPath()%>/css/main.css" />
</head>
<body>
<f:view>
  <h:form id="calcForm">
    <h4>Calculator 3</h4>
    <h:messages infoClass="infoClass" errorClass="errorClass"
        layout="table" globalOnly="true"/>
    <h:panelGrid columns="3" rowClasses="oddRow, evenRow"
          styleClass="formGrid">
        <%-- First Number--%>
        <h:outputLabel value="First Number" for="firstNumber"
                styleClass="#{calculatorController.firstNumberStyleClass}"/>
        <h:inputText id="firstNumber" label="First Number"
          value="#{calculatorController.calculator.firstNumber}" required="true"
          binding="#{calculatorController.firstNumberInput}" />
        <h:message for="firstNumber" errorClass="errorClass"/>

        <%-- Second Number--%>
        <h:outputLabel id="snl" value="Second Number" for="secondNumber"
                styleClass="#{calculatorController.secondNumberStyleClass}"/>
        <h:inputText id="secondNumber" label="Second Number"
          value="#{calculatorController.calculator.secondNumber}" required="true"
          binding="#{calculatorController.secondNumberInput}"/>
        <h:message for="secondNumber" errorClass="errorClass"/>
    </h:panelGrid>
    <div>
      <h:commandButton action="#{calculatorController.add}"  value="Add" />
      <h:commandButton action="#{calculatorController.multiply}"  value="Multiply" />
      <h:commandButton action="#{calculatorController.divide}"  value="Divide" />
      <h:commandButton action="#{calculatorController.clear}"  value="Clear"
          immediate="true"/>
    </div>
  </h:form>


  <h:panelGroup binding="#{calculatorController.resultsPanel}" rendered="false">
  <h4>Results</h4>
   <h:panelGrid columns="1" rowClasses="oddRow, evenRow"
    styleClass="resultGrid">
    <h:outputText value="First Number  #{calculatorController.calculator.firstNumber}"/>
    <h:outputText value="Second Number #{calculatorController.calculator.secondNumber}"/>
    <h:outputText value="Result  #{calculatorController.calculator.result}"/>
   </h:panelGrid>
  </h:panelGroup>
</f:view>

</body>
</html>

Ánh xạ trình điều khiển vào trong tệp faces-config.xml

Tiếp theo, bạn cần phải ánh xạ trình điều khiển mới vào trong faces-config.xml và nội xạ calculator vào nó, như chỉ ra trong Listing 26:

Listing 26. faces-config.xml được cập nhật
<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2">

    <application>
        <message-bundle>messages</message-bundle>
    </application>

    <managed-bean>
        <managed-bean-name>calculatorController</managed-bean-name>
        <managed-bean-class>
            com.arcmind.jsfquickstart.controller.CalculatorController
        </managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
        <managed-property>
            <property-name>calculator</property-name>
            <value>#{calculator}</value>
        </managed-property>
    </managed-bean>
    <managed-bean>
        <managed-bean-name>calculator</managed-bean-name>
        <managed-bean-class>
            com.arcmind.jsfquickstart.model.Calculator
        </managed-bean-class>
        <managed-bean-scope>none</managed-bean-scope>
    </managed-bean>

</faces-config>

Bây giờ bạn đã sửa đổi toàn bộ ứng dụng, tôi sẽ chia nó ra và trình bày các chi tiết.

Phép nội xạ phụ thuộc với JSF

JSF hỗ trợ phép nội xạ phụ thuộc. Bạn có thể nội xạ các bean vào trong các thuộc tính của các bean khác. Do bạn đang nội xạ bean calculator vào trong calculatorController, nên bạn có thể đặt nó vào phạm vi không (none). Phạm vi không (none) có nghĩa là nó sẽ chỉ được tạo ra và không đặt vào trong một phạm vi nào khi nó bắt đầu được tạo ra. Listing 27 cho thấy đoạn mã của faces-config.xml thực hiện nội xạ vào bean quản lý calculator phạm vi không (none):

Listing 27. calculator được quản lý, phạm vi không (none) (không nằm trong phạm vi nào cả)
<managed-bean>
    <managed-bean-name>calculator</managed-bean-name>
    <managed-bean-class>
        com.arcmind.jsfquickstart.model.Calculator
    </managed-bean-class>
    <managed-bean-scope>none</managed-bean-scope>
</managed-bean>

calculatorController được ánh xạ trong phạm vi yêu cầu. Bạn nội xạ calculator vào trong calculatorController bằng cách sử dụng <managed-property> và gán cho giá trị là biểu thức #{calculator} . Điều này dẫn đến việc tạo ra một đối tượng Calculator và nội xạ nó vào trong CalculatorController bằng cách sử dụng phương thức CalculatorControllersetCalculator, như được hiển thị trong Listing 28:

Listing 28. calculator quản lý, phạm vi yêu cầu, và nội xạ với thuộc tính quản lý
<managed-bean>
    <managed-bean-name>calculatorController</managed-bean-name>
    <managed-bean-class>
        com.arcmind.jsfquickstart.controller.CalculatorController
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
        <property-name>calculator</property-name>
        <value>#{calculator}</value>
    </managed-property>
</managed-bean>

CalculatorController sử dụng calculator, vì thế bạn nội xạ calculator. Điều này cho phép bạn sử dụng calculator và giữ nó sạch, không có tính JSF, điều mà một đối tượng mô hình tốt nên có. Tính JSFness chỉ có trong CalculatorController. Sự tách biệt rõ các mối quan tâm khác nhau như thế này giúp làm cho mã của bạn có khả năng thử thách cao hơn và có khả năng sử dụng lại nhiều hơn.

Các thành phần liên kết JSF của CalculatorController

CalculatorController biết được rất nhiều về JSF theo thiết kế. CalculatorController liên kết ba thành phần JSF. Một trong số chúng là resultsPanel, để biểu diễn bảng hiển thị các kết quả của calculator, như chỉ ra trong Listing 29:

Listing 29. Bảng các kết quả (resultsPanel) của CalculatorController's
private UIPanel resultsPanel;

...

public UIPanel getResultsPanel() {
  return resultsPanel;
}

public void setResultsPanel(UIPanel resultPanel) {
  this.resultsPanel = resultPanel;
}

resultsPanel được liên kết vào CalculatorController bởi JSF. Xem thuộc tính binding trong Listing 30:

Listing 30. Liên kết các thành phần vào trong trình điều khiển
<h:panelGroup binding="#{calculatorController.resultsPanel}" rendered="false">
  <h4>Results</h4>
   <h:panelGrid columns="1" rowClasses="oddRow, evenRow"
    styleClass="resultGrid">
    <h:outputText value="First Number  #{calculatorController.calculator.firstNumber}"/>
    <h:outputText value="Second Number #{calculatorController.calculator.secondNumber}"/>
    <h:outputText value="Result  #{calculatorController.calculator.result}"/>
   </h:panelGrid>
/h:panelGroup>

Trong Listing 30, binding="#{calculatorController.resultsPanel}" kết hợp thành phần resultsPanel với binding. Về cơ bản, JSF nhìn thấy biểu thức này, và khi trang Web nạp, nó nội xạ thành phần resultsPanel bằng cách gọi phương thức calculateController.setResultsPanel. Đây là một thuận tiện cho phép bạn thao tác trạng thái của thành phần bằng thủ thuật lập trình mà không cần duyệt qua cây thành phần.

Trên thực tế, những gì JSF thực hiện là gọi phương thức calculateController.getResultsPanel. Nếu cuộc gọi này trả về một thành phần, thì khung nhìn JSF sử dụng thành phần đó. Trái lại, nếu calculateController.getResultsPanel trả về không (null), JSF tạo ra thành phần resultPanel và sau đó gọi phương thức calculateController.setResultPanel với thành phần mới dựa trên các biểu thức liên kết.

Listing 31 chỉ ra cách phương thức add() của CalculateController sử dụng kỹ thuật này như thế nào:

Listing 31. Phương thức add() của CalculateController, tắt resultsPanel
public String add() {

   ...
   
   try {
      calculator.add();
      resultsPanel.setRendered(true);
      ...

   } catch (Exception ex) {
      ...
      resultsPanel.setRendered(false);
   }
   return null;
}

Bạn cần hỗ trợ Ajax ư ? Đừng lo! Hoàn trả từng phần trang để cứu nguy

Nếu bạn sử dụng một khung công tác như JBoss Ajax4Jsf (xem Tài nguyên), bạn có thể dễ dàng cập nhật ứng dụng calculator JSF với rất ít thay đổi để hỗ trợ việc hoàn trả từng phần một trang. (Sự hỗ trợ giống như vậy sẽ đến với JSF 2.0. Ajax4JSF làm việc với JSF 1.1 và JSF 1.2 do đó bạn có thể sử dụng nó ngay bây giờ). Từ phối cảnh người dùng, ứng dụng sẽ có vẻ giống như một applet hoặc một ứng dụng Flex. Và bạn không cần phải viết bất kỳ mã JavaScript nào!

Trong Listing 31, nếu cuộc gọi đến phương thức calculator.add thành công, thì phương thức CalculateController.add gọi phương thức resultsPanel.setRendered(true), để bật hiển thị bảng các kết quả. Nếu cuộc gọi không thành công CalculateController.add sẽ gọi resultsPanel.setRendered(false), và bảng này không còn biểu hiện nữa.

Hãy tạm dừng ở đây. Điều này rất quan trọng: vì JSF là một mô hình thành phần, và các thành phần có trạng thái, các thành phần ghi nhớ trạng thái của mình. Bạn không cần phải bao gồm các logic như bạn đã làm trong ví dụ Calculator trước đó. Hãy báo cho các thành phần không biểu hiện chính nó, và nó sẽ không biểu hiện nữa. Hãy báo cho một thành phần vô hiệu hoá chính nó, và nó bị vô hiệu mỗi khi bạn nạp khung nhìn với điều kiện là khung nhìn đang hoạt động. JSF gần gũi hơn nhiều với một ứng dụng GUI truyền thống so với các khung công tác kiểu Mô hình 2. Bạn cần viết mã càng ít, bạn có thể phát triển một ứng dụng web càng nhanh. JSF đặt ứng dụng vào trong ứng dụng web. Hãy suy nghĩ về nó.

CalculatorController làm việc với các thông báo

JSF có một cơ chế để hiển thị các thông báo trạng thái tới những người sử dụng. CalculateController sử dụng FacesContext để thêm các thông báo tới FacesContext sao cho chúng có thể được hiển thị cho người dùng bằng thẻ <h:messages>.

JSF lưu trữ FacesContext trong một biến ThreadLocal mà bạn có thể truy cập bằng cách gọi phương thức FacesContext.getCurrentInstance(). Phương thức add() sử dụng FacesContext hiện tại để bổ sung thêm các thông báo sẽ có sẵn cho yêu cầu đó, như thấy trong Listing 32:

Listing 32. Phương thức add() của CalculateController bổ sung thêm các thông báo JSF
public String add() {

   FacesContext facesContext = FacesContext.getCurrentInstance();

   try {
      calculator.add();
      facesContext.addMessage(null, new FacesMessage(
            FacesMessage.SEVERITY_INFO, "Added successfully", null));
      ...

   } catch (Exception ex) {
      facesContext.addMessage(null, 
            new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
      //Log the exception as well.
      ...
   }
   return null;
}

Mã lệnh trong Listing 32 bổ sung thêm một FacesMessage tới facesContext với một mức nghiêm trọng là INFO nếu phép tính cộng đã thực hiện thành công hoặc FacesMessage với mức nghiêm trọng là ERROR nếu phép tính cộng đưa ra một trường hợp ngoại lệ.

Các thông báo được hiển thị tới người dùng bằng thẻ <h:messages>, như thấy trong Listing 33:

Listing 33. Hiển thị cho người dùng các thông báo lỗi và thông báo trạng thái
<h:messages infoClass="infoClass" errorClass="errorClass" 
            layout="table" globalOnly="true"/>

Việc thiết lập thuộc tính globalOnly thành true sẽ chỉ hiển thị các thông báo không được gắn với các thành phần cụ thể, giống như những cái bạn đã bổ sung thêm trong Listing 32. Lưu ý rằng bạn sử dụng các kiểu dáng khác nhau cho các thông báo trạng thái và các thông báo lỗi.

CalculatorController sửa chữa lỗi ngoại lệ chia-cho-không

Bởi vì bạn đang làm việc với một mô hình thành phần, bạn có thể thay đổi các giá trị của các thành phần và khởi tạo chúng dựa trên logic hiển thị. Khi phương thức chia mới đưa ra một trường hợp ngoại lệ chia-cho-không, bạn sẽ khôi phục nó bằng cách đặt giá trị secondNumberInput bằng 1.

Trước tiên, bạn cần phải liên kết secondNumberInput với lớp CalculatorController, như thấy trong Listing 34:

Listing 34. Liên kết các thành phần dữ liệu vào: binding="#{calculatorController.resultsPanel}"
<h:inputText id="secondNumber" label="Second Number"
   value="#{calculatorController.calculator.secondNumber}" required="true"
   binding="#{calculatorController.secondNumberInput}"/>

Tiếp theo, bạn sử dụng thành phần secondNumberInput. Nếu bạn nhận được một trường hợp ngoại lệ chia-cho-không, bạn khôi phục lại bằng cách đặt secondNumberInput bằng 1, như được hiển thị trong Listing 35:

Listing 35. Phương thức divide()
public String divide() {

   FacesContext facesContext = FacesContext.getCurrentInstance();

   try {
      calculator.divide();
      facesContext.addMessage(null, new FacesMessage(
            FacesMessage.SEVERITY_INFO, "Divided successfully", null));
      resultsPanel.setRendered(true);

   } catch (Exception ex) {
      if (ex instanceof ArithmeticException) {
         secondNumberInput.setValue(Integer.valueOf(1));
      }
      facesContext.addMessage(null, 
            new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
   }
   return null;
}

Điều quan trọng là phải nghĩ về JSF như là một mô hình thành phần GUI truyền thống nhiều hơn chứ không phải là một phiên bản vui mắt của Mô hình 2. Vô khối khả năng xuất hiện nếu bạn nhớ JSF là một mô hình thành phần. Bạn có thể thiết lập giá trị của secondNumberInput trong Listing 35 vì nó là một đối tượng, không phải là một đoạn mã HTML trong một JSP. Bạn có thể thao tác nó, và nó sẽ nhớ giá trị của mình. Đó là có trạng thái.

Làm việc với các thuộc tính

Hầu hết các thuộc tính của JSF nhận giá trị là các biểu thức, do đó nếu bạn muốn chuyển sang nhãn trường màu đỏ khi có một lỗi xảy ra, bạn có thể dễ dàng làm được như vậy, như được hiển thị trong Listing 36:

Listing 36. Chuyển sang nhãn đỏ
<%-- First Number--%>
<h:outputLabel value="First Number" for="firstNumber" 
    styleClass="#{calculatorController.firstNumberStyleClass}"/>
...

Lưu ý rằng thuộc tính styleClass được gán bằng với biểu thức #{calculatorController.firstNumberStyleClass}, để được liên kết với phương thức trong Listing 37:

Listing 37. Quay lại đúng lớp kiểu dáng khi có một lỗi xảy ra
public String getFirstNumberStyleClass() {
   if (firstNumberInput.isValid()) {
      return "labelClass";
   } else {
      return "errorClass";
   }
}

Listing 37 hỏi thành phần firstNumbedInput rằng đầu vào của nó có hợp lệ không và sau đó thay đổi styleClass để trả về dựa trên câu trả lời có hợp lệ hay không.


Dẫn hướng trong JSF

JSF có một cơ chế dẫn hướng (tương tự với Struts). Cơ chế dẫn hướng của JSF có các kết quả logic để bạn ánh xạ tới khung nhìn logic tiếp theo. Trong phần này, bạn sẽ thêm việc dẫn hướng vào ứng dụng Calculator.

Các quy tắc dẫn hướng

Hình 11 cho thấy các quy tắc dẫn hướng mà bạn sẽ bổ sung thêm vào ứng dụng:

Hình 11. Các quy tắc dẫn hướng để bổ sung thêm vào ứng dụng Calculator
Quy tắc dẫn hướng

Thật thú vị khi có các công cụ giúp bạn trình bày luồng lưu thông của ứng dụng Web của bạn. Nhiều IDE cung cấp các công cụ để vẽ các quy tắc dẫn hướng của một ứng dụng JSF. Hình 12 cho thấy công cụ trình bày các quy tắc dẫn hướng của Eclipse JEE:

Hình 12. Trình bày các quy tắc dẫn hướng trong Eclipse
Quy tắc dẫn hướng trong Eclipse

Sau khi bạn thực hiện xong phần này, các Hình 11 và 12 sẽ mang lại nhiều ý nghĩa hơn.

Có lẽ bạn không cần phải dẫn hướng

Có những khi bạn cần phải di chuyển từ một khung nhìn tới khung nhìn tiếp theo. Tuy nhiên, nhiều khung công tác thành phần JSF có các khung nhìn dạng cây và các khung nhìn dạng phiếu. Mỗi khung nhìn có thể có 10 phiếu, và khi nhấn chuột vào một phiếu sẽ nạp một phần khác nhau của khung nhìn. Khi sử dụng thiết kế này, bạn có thể dễ dàng tạo ra một ứng dụng lớn mà không bao giờ phải viết các quy tắc dẫn hướng, do toàn bộ ứng dụng nằm trong một khung nhìn. Các nhánh của cách tiếp cận này, bao gồm các cánh vòng tránh và các cách làm tốt nhất, nằm ngoài phạm vi của hướng dẫn này.

Đầu tiên bạn sẽ thêm một trang chủ để liên kết với trang calculator. Sau đó, bạn sẽ phân chia trang calculator thành hai trang: một để hiển thị khung nhìn calculator, và một để hiển khung nhìn các kết quả. Bạn cũng sẽ cần các quy tắc dẫn hướng qua lại giữa các trang calculator, trang các kết quả, và trang chủ.

Liên kết từ trang chủ tới trang calculator

Bạn có thể liên kết từ trang chủ tới trang calculator theo ba cách:

  • Qua một commandLink và một quy tắc dẫn hướng
  • Qua một commandLink và một quy tắc dẫn hướng bằng cách sử dụng một trang chuyển hướng
  • Qua một outputLink

Liên kết thông qua commandLink và một quy tắc dẫn hướng được thực hiện bằng cách thêm một quy tắc dẫn hướng vào tệp tin faces-config.xml, như được hiển thị trong Listing 38:

Listing 38. Quy tắc dẫn hướng đã định nghĩa trong faces-config.xml
<navigation-rule>
    <navigation-case>
        <from-outcome>CALCULATOR</from-outcome>
        <to-view-id>/pages/calculator.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

Listing 38 nói rõ rằng bất kỳ hành động nào có trả về CALCULATOR sẽ làm cho JSF nạp /pages/calculator.jsp như là khung nhìn tiếp theo. Quy tắc này là động, trong đó bất kỳ hành động nào từ bất kỳ nơi nào trong ứng dụng có trả về CALCULATOR sẽ đi tới /pages/calculator.jsp (trừ khi áp dụng một quy tắc cụ thể hơn — trong JSF bạn có thể thêm một <from-view-id> rồi làm cho quy tắc áp dụng chỉ cho khung nhìn đó, tôi sẽ đề cập trường hợp quy tắc cụ thể hơn ở đoạn sau trong phần này). Listing 38 tương tự với một chuyển tiếp toàn cục theo cách nói của Struts.

Trong form này, thêm vào commandLink như hiển thị trong Listing 39, Nó phải nằm trong một <h:form>:

Listing 39. Sử dụng commandLink trong trang chủ
<h:form>
    <h:panelGrid columns="1">
    <h:commandLink action="CALCULATOR" value="Calculator Application"/>
        ...

Điều này có tác dụng như bạn mong đợi: ứng dụng Calculator nạp vào trong trình duyệt. Người sử dụng có thể thấy khó hiểu do URL trong trình duyệt vẫn còn là http://localhost:8080/calculator3/home.jsf mặc dù khung nhìn calculator đang hiển thị. Điều này có thể gây rối, đặc biệt là cho những người dùng có hiểu biết Web. Và bất kỳ người dùng nào người muốn đánh dấu phần calculator của ứng dụng sẽ không biết liên kết thực sự.

Một cách để sửa lỗi này là sử dụng một redirect (sự chuyển hướng) trong quy tắc dẫn hướng trong faces-config.xml, như hiển thị trong Listing 40:

Listing 40. Quy tắc dẫn hướng với phần tử redirect
<navigation-rule>
    <navigation-case>
        <from-outcome>CALCULATOR_REDIRECT</from-outcome>
        <to-view-id>/pages/calculator.jsp</to-view-id>
        <redirect/>            <!-- LOOK HERE -->
    </navigation-case>
</navigation-rule>

Một commandLink có thể sử dụng quy tắc dẫn hướng bằng cách chỉ rõ chuỗi ký tự kết quả đầu ra như là giá trị của thuộc tính action (hành động). Khi được nhấn vào, liên kết trong Listing 41 đưa người dùng đến trang calculator:

Listing 41. Sử dụng quy tắc dẫn hướng với redirect
<h:commandLink action="CALCULATOR_REDIRECT" value="Calculator Application (redirect)"/>

Điều này giải quyết được vấn đề, nhưng phải trả giá cho một cú truy cập thêm nhỏ tới máy chủ, trên một kết nối chậm có thể kéo khá dài. Nhưng nếu bạn đang xây dựng một ứng dụng nội bộ, thì cú truy nhập này sẽ không quan trọng.

Bạn không phải truy nhập máy chủ hai lần nếu bạn không chủ tâm kết nối trực tiếp tới trang web mà bạn muốn nạp, như hiển thị trong Listing 42:

Listing 42. Liên kết trực tiếp với outputLink
<h:outputLink value="pages/calculator.jsf">
    <h:outputText value="Calculator Application (outputlink)"/>
</h:outputLink>

Listing 42 liên kết trực tiếp đến khung nhìn kế tiếp. Trong một kiến trúc Mô hình 2 và trong JSF, liên kết trực tiếp từ một khung nhìn này tới một khung nhìn tiếp theo được coi là hình thái tồi. Thông thường, trình điều khiển cần có một cơ hội để khởi tạo mô hình cho khung nhìn tiếp theo, vì vậy tốt hơn là thông qua một phương thức hành động. Tuy nhiên, Listing 42 tạo ra một liên kết, và URL đúng sẽ hiện lên trong trình duyệt.

Khả năng để đánh dấu các ứng dụng JSF đã là một vấn đề trong một thời gian. Một số khung công tác đã giải quyết vấn đề này, bao gồm JBoss Seam. Đây là một vấn đề nhỏ trong một thời gian và cũng sẽ được giải quyết trong JSF 2.

Dẫn hướng đến trang các kết quả

Sau khi bạn thực hiện một phép tính trong máy tính, bạn muốn khung nhìn tiếp theo là trang các kết quả mới. Để thực hiện điều này, thêm quy tắc dẫn hướng trong Listing 43:

Listing 43. Quy tắc dẫn hướng từ tất cả các hành động trong khung nhìn máy tính tới trang kết quả
<navigation-rule>
    <display-name>Calculator View</display-name>
    <from-view-id>/pages/calculator.jsp</from-view-id>
    <navigation-case>
        <from-outcome>results</from-outcome>
        <to-view-id>/pages/results.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

Listing 43 nói rằng nếu khung nhìn hiện nay là khung nhìn máy tính (calculator.jsp), và bất kỳ hành động nào trả về các kết quả (results), thì khung nhìn JSF tiếp theo sẽ là trang các kết quả (results.jsp). JSF xem xét giá trị trả về từ các phương thức hành động và biến đổi chúng thành một chuỗi ký tự (nếu chúng khác null) và sử dụng chuỗi ký tự đó để chọn khung nhìn tiếp theo. Kiểu trả về có thể là đối tượng bất kỳ vì phương thức toString của nó sẽ được gọi. (Nhiều người sử dụng các kiểu Enum).

Thay đổi tất cả các phép tính của bạn để trả về results, như trong phương thức cộng add() hiển thị trong Listing 44:

Listing 44. Các phương thức hành động bây giờ trả về các kết quả
public String add() {

    FacesContext facesContext = FacesContext.getCurrentInstance();

    try {
        calculator.add();
        facesContext.addMessage(null, new FacesMessage(
                FacesMessage.SEVERITY_INFO, "Added successfully", null));

    } catch (Exception ex) {
        facesContext.addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
    }
    return "results";
}

Lưu ý rằng phương thức add() trả về các kết quả, và các kết quả được ánh xạ đến results.jsp. Lưu ý rằng nút Cancel (Huỷ) hiện không trả về các kết quả. Nếu một hành động trả về một giá trị mà không được ánh xạ đến một quy tắc dẫn hướng, JSF lưu lại ở khung nhìn hiện tại.

Bạn có thể nhận được cụ thể hơn, như trong Listing 45:

Listing 45. Các ánh xạ khớp với các hành động
<navigation-rule>
    <display-name>Calculator View</display-name>
    <from-view-id>/pages/calculator.jsp</from-view-id>
    <navigation-case>
        <from-action>#{calculatorController.add}</from-action>
        <from-outcome>results</from-outcome>
        <to-view-id>/pages/results.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

Nhưng bây giờ bạn cần phải có một ánh xạ cho mỗi phương thức. Chán quá! Đó thật là không lặp lại chính mình (DRY ).

Bạn cũng muốn thêm một commandButton trở về trang chủ vào trang calculator, như hiển thị trong Listing 46:

Listing 46. Các hành động của Calculator với liên kết trở về trang chủ
<div>
  <h:commandButton action="#{calculatorController.add}" value="Add" />
  <h:commandButton action="#{calculatorController.multiply}" value="Multiply" />
  <h:commandButton action="#{calculatorController.divide}" value="Divide" />
  <h:commandButton action="#{calculatorController.clear}" value="Clear" immediate="true"/>
  <h:commandButton action="HOME" value="Home" immediate="true"/>
</div>

Lưu ý rằng liên kết trở về trang chủ chỉ rõ giá trị của hành động là HOME. Listing 47 cho thấy quy tắc dẫn hướng ánh xạ kết quả HOME mà liên kết trở về trang chủ sử dụng đến trang chủ:

Listing 47. Ánh xạ quy tắc dẫn hướng đến trang chủ
<navigation-rule>
    <navigation-case>
        <from-outcome>HOME</from-outcome>
        <to-view-id>/home.jsp</to-view-id>
        <redirect/>            
    </navigation-case>
</navigation-rule>

Lưu ý rằng các commandButton và các commandLink làm việc như nhau đối với các quy tắc dẫn hướng và xử lý hành động.

Trang các kết quả có một liên kết quay về trang calculator và một liên kết quay về trang chủ, như hiển thị trong Listing 48:

Listing 48. Trang kết quả có thể về trang chủ hoặc quay lại trang calculator
<h:panelGrid columns="1" rowClasses="oddRow, evenRow">
    <h:commandLink action="calculator" value="Return to the calculator page"/>
    <h:commandLink action="HOME" value="Go to the home page"/>
    <h:commandLink action="calculatorMain" value="Go to main calculator page"/>
</h:panelGrid>

Bạn có thể quay lại trang calculator theo hai cách. Một cách giống như cách bạn đã thấy, được hiển thị trong Listing 49:

Listing 49. Ánh xạ quay lại trang calculator
<navigation-rule>
    <display-name>Results Page</display-name>
    <from-view-id>/pages/results.jsp</from-view-id>
    <navigation-case>
        <from-outcome>calculator</from-outcome>
        <to-view-id>/pages/calculator.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

Listing 49 nói rằng nếu bạn ở trang các kết quả (/pages/results.jsp) và một hành động trả về calculator, thì hãy đi tới trang máy tính (/pages/calculator.jsp). Nếu bạn muốn một điều gì đó không cụ thể bằng Listing 49, nhưng lại cụ thể hơn so với kiểu chuyển tiếp toàn cục mà tôi đề cập từ đầu, bạn có thể sử dụng Listing 50:

Listing 50. Một ánh xạ khác quay lại trang máy tính từ bất kỳ nơi đâu dưới trang/*
<navigation-rule>
    <from-view-id>/pages/*</from-view-id>
    <navigation-case>
        <from-outcome>calculatorMain</from-outcome>
        <to-view-id>/pages/calculator.jsp</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>

Listing 50 cho phép bạn định nghĩa các khu vực logic của ứng dụng của bạn và có kết quả để áp dụng chỉ cho những nơi đó.


Kết luận

Hướng dẫn này đã trình bày JSF và đã cho thấy rằng nó là một mô hình thành phần. Bởi vì JSF là một khung công tác thành phần như Swing, SWT, hay AWT, nó làm cho việc phát triển ứng dụng web giống nhiều hơn với việc phát triển GUI truyền thống và ít giống với việc phát triển Web truyền thống. Các ứng dụng được viết bằng JSF là ngắn hơn và dễ hiểu và bảo trì hơn, so với các ứng dụng được viết theo phong cách Mô hình 2 điển hình. Hình như có rất nhiều mối quan tâm đến JSF trong cộng đồng Java và yêu cầu công việc phát triển lên cho phù hợp.

JSF 2, đang định hình, kết hợp chặt chẽ các khái niệm Facelets, bổ sung thêm hỗ trợ Ajax tự nhiên, và làm cho việc phát triển thành phần JSF dễ dàng hơn. JSF 2 sẽ nâng cao đầu tư của bạn vào JSF. Mô hình mới có khả năng hoàn trả một phần trang nhờ Ajax, mà hiện nay đang có được nhờ các công cụ như Ajax4JSF.

Điều này không phải để nói rằng, JSF hiện không có các đối thủ cạnh tranh. Trong số các mô hình thành phần phía máy chủ, Tapestry 5 trông chắc chắn vậy mà không tương thích với Tapestry 4. Wicket cũng rất thú vị, nhưng đã không giành được đủ động lực cho nhiều người để ý đến nó.

Sau đó, còn có các khung công tác Java phía máy chủ không thành phần. Người ta đã nói rằng, Struts 2.x đã làm một công việc to lớn trong việc cải thiện WebWork, và một số người mong đợi Struts 2.1.x mở cửa tới những điều to lớn hơn và tốt đẹp hơn, còn Struts 2 thực sự dựa trên WebWork chứ không phải là Struts 1.x. Spring MVC tiếp tục phát triển rất nhanh và là một lựa chọn tốt nếu bạn chọn một khung công tác Web phía máy chủ không có thành phần GUI.

Cuối cùng, có những hoạt động thuần tuý phía khách để uỷ quyền cho các dịch vụ trên máy chủ, như Google Web Toolkit (GWT) và phần mềm Adobe Flex. (Kiến trúc khác với JSF, nhưng các ứng dụng đích là như nhau.) Mỗi phần mềm có những ưu thế và các mặt hạn chế riêng, nhưng có thể tác động đến việc chấp nhận JSF.

Tuy nhiên, JSF có khả năng tiếp tục làm việc tốt vì nó là tiêu chuẩn cho Java EE và có một cộng đồng rất năng động ở phía sau nó. Nhu cầu về JSF vượt quá nhu cầu về Tapestry, Spring MVC, Java Flex, và GWT. JSF 2.0 có thể thúc đẩy JSF tiến lên hơn nữa.

Phần 2 trong loạt bài hướng dẫn này sẽ đề cập đến vòng đời của JSF và sẽ làm việc với các trình duyệt tính hợp lệ (validator), các trình biến đổi (converter) , trình nghe pha, và các tính năng cao cấp khác của JSF.


Tải về

Mô tảTênKích thước
Mã ví dụ cho bài báo này1j-jsf1.zip112KB

Ghi chú

  1. Để tìm các chỉ dẫn và các tệp tin JAR để chạy các mã nguồn của hướng dẫn này với Eclipse JEE, Tomcat, và Maven 2, xem Tài nguyên.

Tài nguyên

Học tập

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, SOA và dịch vụ Web
ArticleID=383340
ArticleTitle=Khởi đầu với JavaServer Faces 1.2, Phần 1: Xây dựng các ứng dụng cơ bản
publish-date=05202009