通过postValidate进行JSF交叉字段validation,而无需在支持bean中按名称查找组件

我正在构建一个登录表单复合组件。 使用它的页面将传递一个将validation用户名和密码的事件处理程序。 通常(不使用复合组件)当我们通过postValidate执行交叉字段validation时,事件处理程序必须按名称查找字段的组件。 validation器最好不要这样做,因为这些是应该抽象的组件的内部细节。

知道如何在不了解复合组件的内部细节的情况下在postValidate处理程序中获取用户名和密码字段的转换值吗?

更新:这一点不是为了避免按名称查找组件,而是能够以不需要使用页面和/或bean来了解内部的方式跨域validation复合组件的字段组件的详细信息。

这可以做到。 在以下代码中,请特别注意复合组件中的postValidate事件和后备组件中的postValidate方法。 注意它如何解析MethodExpression属性并调用传入方法。

这是复合组件:

                      

支持组件:

 @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; } 

使用登录表单的页面:

        Sign In       

最后,页面的支持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方法不会给你留下很多选择。

我更喜欢的选项是对表单的最后一个组件进行validation,然后使用binding和f:attribute传入其他组件。

例如

     

然后在validation器中,您可以从UIInput中获取其他组件:

 UIComponent field1 = field2.getAttributes().get("field1") 

我使用JsfWarn来解决类似的问题,我认为它以更清洁的方式解决了你的问题。

与JSFvalidation器不同,在模型更新后,在调用应用程序之后,在渲染响应之前执行WarningValidators,因此您只需访问应用程序以获得validation结果。

 @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.")); } } } 

并将validation器添加到目标字段: