我建立一个登录表单复合材料部件。 使用它的页面将传递一个事件处理程序,将验证用户名和密码。 通常(不使用复合部件)时,我们通过执行跨字段验证postValidate
,事件处理程序具有通过名称查找字段的部件。 这将是最好的验证不要这样做,因为这些是应该被抽象组件的内部细节。
任何想法,我怎么可能会得到一个用户名和密码字段的转换后的值postValidate
处理器在不知道的合成成分的内部细节?
更新:这点是不避仰视的名字成分可言,但要能够跨场验证复合材料部件的领域中,不要求使用页面和/或豆知道内部的方式组件的细节。
这是可以做到。 在下面的代码,特别注意到的的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;
}
在f:event postValidate
方法不离开你很多的选择。
我更喜欢选项做表格的最后一个组件上的验证,然后在通过使用绑定和f其他组件:属性。
例如
<h:inputText id="field1" binding="#{field1}" ... />
<h:inputText id="field2" validator="#{...}">
<f:attribute name="field1" value="#{field1}"/>
</h:inputText>
然后在你的验证器,你可以抓住关UIInput其他组件:
UIComponent field1 = field2.getAttributes().get("field1")
我已经使用JsfWarn了类似的问题,我认为它解决了一个更清洁的方式您的问题。
不像JSF验证模型已被更新后的应用程序已被调用之前的WarningValidators进行渲染响应,所以你可以简单地访问您的验证结果的应用。
@Named
public class BarWarningValidator implements WarningValidator{
@Inject
private MyValidationBean validationBean;
@Override
public void process(FacesContext context, UIInput component, ValidationResult validationResult) {
if(!validationBean.isBarValid()) {
validationResult.setFacesMessage(new FacesMessage(FacesMessage.SEVERITY_WARN, "FooBar", "This is a warning."));
}
}
}
并添加验证到目标领域:
<h:outputLabel for="bar" value="Default warning:" />
<h:inputText id="bar">
<jw:warning validator="#{barWarningValidator}" />
<f:ajax event="change" render="..." />
</h:inputText>
<h:message for="bar" />
文章来源: JSF cross field validation via postValidate without looking up components by name in backing bean