这是可以做到的。在以下代码中,请特别注意postValidate
复合组件中的事件和postValidate
支持组件中的方法。请注意它如何解析MethodExpression
属性并调用传入的方法。
这是复合组件:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<!-- Login form. -->
<cc:interface componentType="com.example.LoginForm">
<cc:attribute name="emailAddress" type="java.lang.String" required="true"/>
<cc:attribute name="rememberMe" type="java.lang.Boolean" required="true"/>
<cc:attribute name="checkCredentials"
method-signature="void checkCredentials(java.lang.String,java.lang.String,java.lang.String)"
shortDescription="Parameters are clientId, username and password. If credentials are invalid, attach a FacesMessage to the component specified by clientId."
required="true"/>
<cc:attribute name="actionListener" method-signature="void actionListener()" required="true"/>
<cc:attribute name="registerOutcome" type="java.lang.String" required="true"/>
<cc:attribute name="recoverPasswordOutcome" type="java.lang.String" required="true"/>
<cc:attribute name="headerTitle" type="java.lang.String" default="Sign In"/>
<cc:attribute name="emailAddressLabel" type="java.lang.String" default="Email address:"/>
<cc:attribute name="passwordLabel" type="java.lang.String" default="Password:"/>
<cc:attribute name="rememberMeLabel" type="java.lang.String" default="Stay signed in on this machine"/>
<cc:attribute name="loginLabel" type="java.lang.String" default="Sign In"/>
<cc:attribute name="recoverPasswordLabel" type="java.lang.String" default="Forgot password?"/>
<cc:attribute name="emailAddressRequiredMessage" type="java.lang.String" default="Email address required"/>
<cc:attribute name="passwordRequiredMessage" type="java.lang.String" default="Password required"/>
<cc:attribute name="registerLabel" type="java.lang.String" default="Register"/>
</cc:interface>
<cc:implementation>
<h:outputStylesheet library="components/example/login-form" name="style.css"/>
<div id="#{cc.clientId}">
<h:form id="form">
<f:event type="postValidate" listener="#{cc.postValidate}"/>
<div style="margin-top:10px;">
<p:panel header="#{cc.attrs.headerTitle}" styleClass="loginPanel">
<div class="login-form_errorContainer">
<p:messages rendered="#{facesContext.maximumSeverity.ordinal ge 2}"/>
</div>
<h:panelGrid columns="3">
<h:outputText styleClass="login-form_label" value="#{cc.attrs.emailAddressLabel}"/>
<h:panelGroup styleClass="login-form_cell">
<h:inputText id="emailAddress"
value="#{cc.attrs.emailAddress}"
required="true"
requiredMessage="#{cc.attrs.emailAddressRequiredMessage}"
styleClass="login-form_field"
immediate="true"/>
</h:panelGroup>
<h:panelGroup/>
<h:outputText styleClass="login-form_label" value="#{cc.attrs.passwordLabel}"/>
<h:panelGroup styleClass="login-form_cell">
<h:inputSecret id="password"
value="#{cc.attrs.password}"
required="true"
requiredMessage="#{cc.attrs.passwordRequiredMessage}"
styleClass="login-form_field"
immediate="true"/>
</h:panelGroup>
<h:link styleClass="login-form_link" value="#{cc.attrs.recoverPasswordLabel}" outcome="#{cc.attrs.recoverPasswordOutcome}"/>
<h:panelGroup/>
<p:selectBooleanCheckbox value="#{cc.attrs.rememberMe}" itemLabel="#{cc.attrs.rememberMeLabel}" immediate="true"/>
<h:panelGroup/>
<h:panelGroup/>
<h:panelGroup>
<p:commandButton id="submitForm" value="#{cc.attrs.loginLabel}" actionListener="#{cc.attrs.actionListener}" update="form"/>
<span class="login-form_or">or</span>
<h:link styleClass="login-form_link" value="#{cc.attrs.registerLabel}" outcome="#{cc.attrs.registerOutcome}"/>
</h:panelGroup>
<h:panelGroup/>
</h:panelGrid>
</p:panel>
</div>
</h:form>
</div>
</cc:implementation>
</html>
支持组件:
@FacesComponent("com.example.LoginForm")
public class LoginFormComponent extends UIInput implements NamingContainer
{
@Override
protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) throws ConverterException
{
UIInput emailAddressComponent = (UIInput) findComponent(EMAIL_ADDRESS_ID);
UIInput passwordComponent = (UIInput) findComponent(PASSWORD_ID);
String emailAddress = (String) emailAddressComponent.getValue();
String password = (String) passwordComponent.getValue();
return new LoginFormValue(emailAddress, password);
}
public void postValidate(ComponentSystemEvent e) {
FacesContext ctx = getFacesContext();
// Don't validate credentials if the username and/or password fields are invalid.
if (!ctx.getMessageList(EMAIL_ADDRESS_ID).isEmpty() || !ctx.getMessageList(PASSWORD_ID).isEmpty())
{
return;
}
LoginFormValue value = (LoginFormValue) getConvertedValue(null, null);
MethodExpression checkCredentials = (MethodExpression) getAttributes().get(CHECK_CREDENTIALS_ATTRIBUTE_NAME);
checkCredentials.invoke(ctx.getELContext(), new Object[]{getClientId(), value.getEmailAddress(), value.getPassword()});
}
@Override
public String getFamily()
{
return "javax.faces.NamingContainer";
}
public static final String CHECK_CREDENTIALS_ATTRIBUTE_NAME = "checkCredentials";
public static final String EMAIL_ADDRESS_ID = "form:emailAddress";
public static final String PASSWORD_ID = "form:password";
}
完整性类LoginFormValue
:
public class LoginFormValue
{
public LoginFormValue(String emailAddress, String password)
{
this.emailAddress = emailAddress;
this.password = password;
}
public String getEmailAddress()
{
return emailAddress;
}
public String getPassword()
{
return password;
}
private String emailAddress;
private String password;
}
使用登录表单的页面:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ex="http://java.sun.com/jsf/composite/components/example">
<h:head>
<title></title>
</h:head>
<h:body>
<ui:composition template="/WEB-INF/templates/myLayout.xhtml">
<ui:define name="windowTitle">Sign In</ui:define>
<ui:define name="body">
<ex:login-form emailAddress="#{loginBean.emailAddress}"
rememberMe="#{loginBean.rememberMe}"
checkCredentials="#{loginBean.checkCredentials}"
actionListener="#{loginBean.submit()}"
recoverPasswordOutcome="recover-password"
registerOutcome="signup"/>
</ui:define>
</ui:composition>
</h:body>
</html>
最后,页面的支持 bean:
@Named
@RequestScoped
public class LoginBean implements Serializable
{
public String getEmailAddress()
{
return emailAddress;
}
public void setEmailAddress(String emailAddress)
{
this.emailAddress = emailAddress;
}
public boolean isRememberMe()
{
return rememberMe;
}
public void setRememberMe(boolean rememberMe)
{
this.rememberMe = rememberMe;
}
/** Action listener for login-form. Called after validation passes. */
public void submit()
{
User user = userDao.findByEmailAddress(emailAddress);
userRequestBean.login(user.getUserId());
// Remember me
if (!rememberMe)
{
return;
}
// Handle rememberMe here (create a cookie, etc.)
}
/** Called by the backing component's postValidate event handler */
public void checkCredentials(String clientId, String emailAddress, String password)
{
if (!securityEjb.checkCredentials(emailAddress, password))
{
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Incorrect email address/password", null);
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.addMessage(clientId, message);
ctx.renderResponse();
}
}
private String emailAddress = "";
private boolean rememberMe = true;
@Inject
private UserRequestBean userRequestBean;
@EJB
private SecurityEjb securityEjb;
@EJB
private UserDao userDao;
@EJB
private LoginCookieDao loginCookieDao;
}