1.技術問題
JSF 的 composite component (複合元件) 的功能可以組合現有 UIComponent 成為新的、重複使用的元件。可以組合成的複合元件使用、驗證器、轉換器、JSF 系統事件、及 AJAX。 完成的複合元件會包含:- facelet: 檔案名稱為新的標籤的名稱,其內容定義標籤的屬性及組合現有的元件。
- java class: 支援此複合元件執行所需要的 java 程式碼。
h:outputLabel
, h:inputText
, h:commandButton
等三個元件,再加上一個驗證器成為新的元件專門用來輸入電子郵件。
當你完成複合元件時,你可以在現有 Web 專案的 facelet 中直接使用此元件的標籤。但是,如何將此複合元件變成一個獨立的程式庫供其他的 Web 專案使用呢?
2.參考解答
複合元件可以被包裝成 jar 檔,供其他的 JSF 專案使用。複合元件被視為應用程式的資源之一,預設需放在 JSF 的 Resource Library 中。如果是在 Web 專案,Resource Library 的位置在
[webroot]/resource
下;當包裝成 war 檔後,其位置在 WEB-INF/resources
[2]。如果是在 Java Application 專案,Resource Library 的位置在 META-INF/resources
下。預設資源位置的名稱空間 URL 為 xmlns:my="http://xmlns.jcp.org/jsf/composite/
。你也可在 META-INF 下新增一個
[component_name].taglib.xml
檔案自訂自己的名稱空間 URL[3]。
綜合上述,在 jar 檔中的結構為:
- /META-INF/faces-config.xml
- /META-INF/[component_name].taglib.xml
- /META-INF/resources/comp (library with composite components)
- /META-INF/resources/css (Stylesheets)
- /META-INF/resources/images
- /META-INF/resources/js
- /package_name/class_name.class
Application Configuration Resource File
Application Configuration Resource 檔案,或者 faces-config.xml,是用來註冊及設定物件(object)和資源(resource),並且用來定義瀏覽規則(navigation rule)[4]。 若複合元件中內有使用 annotation 的 java class,則必須在 META-INF 目錄下使用 faces-config.xml,告知 JSF 在 class path 下的類別使用 annotation 註記。此時的 faces-config.xml 的內容為:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version='1.0' encoding='UTF-8'?> | |
<faces-config version="2.2" | |
xmlns="http://xmlns.jcp.org/xml/ns/javaee" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"> | |
</faces-config> |
當應用程式載入時,JSF 會依照以下的方式來尋找一個或多個設定檔案[4]:
- 對於 /WEB-INF/lib 目錄下的 jar 檔,尋找此 jar 檔內的 /META-INF/faces-config.xml。如果此檔案存在,會載入此 faces-config.xml 內定義的物件和資源。JSF 會掃描 jar
- web.xml 內 javax.faces.application.CONFIG_FILES 參數所指定的一個或多個設定檔案
- 在應用程式的 /WEB-INF/faces-config.xml。
3.實作示範
實作一個 DateRangeInput 複合元件,可以輸入起始及結束日期。檢查規則:結束日期不能早於起始日期。日期輸入的元件為 p:calendar
(PrimeFaces 的 Calendar 元件)。
Task 1. 製作 DateRangeInput 複合元件
- 在 Netbeans 中開啟新的 Java Application 專案
- 在專案的 Source Packages 下建立一套件 META-INF.resources.comp
- 在 META-INF.resources.comp 下,放入先前完成的 facelet: dateRange.xml
- 在 Source Packages 下建立 META-INF 套件。
- 在 META-INF 下建立 faces-config.xml。
- 建立套件 /validator。
- 將驗證器 DateRangeValidator.java 放置到套件 /validator。
- Clean and Build 你的 Java Application 專案,便會產生 jar 檔。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version='1.0' encoding='UTF-8' ?> | |
<!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" | |
xmlns:h="http://xmlns.jcp.org/jsf/html" | |
xmlns:cc="http://xmlns.jcp.org/jsf/composite" | |
xmlns:p="http://primefaces.org/ui" | |
xmlns:f="http://xmlns.jcp.org/jsf/core"> | |
<h:head> | |
</h:head> | |
<h:body> | |
<cc:interface> | |
<cc:attribute name="startDate" /> | |
<cc:attribute name="endDate" /> | |
<cc:attribute name="errorMsg" /> | |
</cc:interface> | |
<cc:implementation> | |
<p:outputLabel value="開始"/> | |
<p:calendar id="startDate" | |
pattern="yyyy/MM/dd" | |
binding="#{startDateComponent}" | |
value="#{cc.attrs.startDate}"> | |
</p:calendar> | |
<p:outputLabel value="結束日期"/> | |
<p:calendar id="endDate" | |
pattern="yyyy/MM/dd" | |
value="#{cc.attrs.endDate}"> | |
<f:validator validatorId="dateRangeValidator" /> | |
<f:attribute name="startDateComponent" | |
value="#{startDateComponent}" /> | |
<f:attribute name="errorMsg" | |
value="#{cc.attrs.errorMsg}" /> | |
<p:ajax event="change" | |
process="startDate endDate" | |
update="endDateMsg" /> | |
<p:ajax event="dateSelect" | |
process="startDate endDate" | |
update="endDateMsg" /> | |
</p:calendar> | |
<p:message id="endDateMsg" for="endDate" /> | |
</cc:implementation> | |
</h:body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version='1.0' encoding='UTF-8'?> | |
<faces-config version="2.2" | |
xmlns="http://xmlns.jcp.org/xml/ns/javaee" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"> | |
</faces-config> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package validator; | |
import java.util.Date; | |
import javax.faces.application.FacesMessage; | |
import javax.faces.component.UIComponent; | |
import javax.faces.component.UIInput; | |
import javax.faces.context.FacesContext; | |
import javax.faces.validator.FacesValidator; | |
import javax.faces.validator.Validator; | |
import javax.faces.validator.ValidatorException; | |
/** | |
* Validate the end date cannot be earlier than the start date. The UIComponent | |
* for entering the start date is passed by the attribute with name | |
* {@code startDate}. | |
* | |
* | |
* Reference: | |
* {@link http://stackoverflow.com/questions/19093192/date-range-validation} | |
* | |
* @author hychen39@gmail.com | |
*/ | |
@FacesValidator("dateRangeValidator") | |
public class DateRangeValidator implements Validator { | |
public static final String START_DATE_COMPONENT = "startDateComponent"; | |
public static final String ERROR_MESSAGE = "errorMsg"; | |
@Override | |
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { | |
// check the value of the end date | |
if (value == null) { | |
return; | |
} | |
// get the start date | |
UIInput startDateComponent = (UIInput) component.getAttributes().get(START_DATE_COMPONENT); | |
if (!startDateComponent.isValid()) { | |
return; | |
} | |
Date startDate = (Date) startDateComponent.getValue(); | |
if (startDate == null) { | |
return; | |
} | |
// get the end date | |
Date endDate = (Date) value; | |
// get the error message | |
String msg = (String) component.getAttributes().get(ERROR_MESSAGE); | |
String errMsg = (msg != null)? msg: "The end date cannot be before the start date."; | |
// validate the end date | |
if (startDate.after(endDate)) { | |
((UIInput)component).setValid(false); | |
String clientId = component.getClientId(); | |
FacesContext.getCurrentInstance().addMessage(clientId, | |
new FacesMessage(errMsg)); | |
} | |
} | |
} |
Task 2. 在 Web 專案中使用複合元件
- 建立新的 JSF Web 專案
- 加入 PrimeFaces 及 符合元件的 jar 檔。
- 建立一 Managed Bean,其具有 startDate 及 endDate 兩個 properties。
- 在 index.xhtml 中加入複合元件的 namespace url,及複合元件的標籤。
- Build and Deploy 專案
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Date; | |
import javax.inject.Named; | |
import javax.enterprise.context.RequestScoped; | |
/** | |
* | |
* @author hychen39@gmail.com | |
*/ | |
@Named(value = "dateRangeBean") | |
@RequestScoped | |
public class DateRangeBean { | |
private Date startDate; | |
private Date endDate; | |
public Date getStartDate() { | |
return startDate; | |
} | |
public void setStartDate(Date startDate) { | |
this.startDate = startDate; | |
} | |
public Date getEndDate() { | |
return endDate; | |
} | |
public void setEndDate(Date endDate) { | |
this.endDate = endDate; | |
} | |
/** | |
* Creates a new instance of DateRangeBean | |
*/ | |
public DateRangeBean() { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version='1.0' encoding='UTF-8' ?> | |
<!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" | |
xmlns:h="http://xmlns.jcp.org/jsf/html" | |
xmlns:my="http://xmlns.jcp.org/jsf/composite/comp"> | |
<h:head> | |
<title>Facelet Title</title> | |
</h:head> | |
<h:body> | |
<h:form> | |
<my:dateRange startDate="#{dateRangeBean.startDate}" | |
endDate="#{dateRangeBean.endDate}"/> | |
</h:form> | |
</h:body> | |
</html> | |
4.參考資料
[1] Java EE6> Packaging JSF facelets (xhtml) and ManagedBeans as JAR, http://stackoverflow.com/questions/6104498/java-ee6-packaging-jsf-facelets-xhtml-and-managedbeans-as-jar[2] Packaging composite component in JSF2 with Netbeans 7.0.1, Maven http://stackoverflow.com/questions/8295935/packaging-composite-component-in-jsf2-with-netbeans-7-0-1-maven
[3] JSF2 Reuse composite components – Packaging in a Jar file - English Article http://www.jcafe.info/2011/02/jsf2-reuse-composite-components.html
[4] Application Configuration Resource File, http://docs.oracle.com/javaee/6/tutorial/doc/bnawp.html
[5] Sharing JSF 2 Composite Components, https://digitaljoel.nerd-herders.com/2009/12/14/sharing-jsf-2-composite-components/
沒有留言:
張貼留言