JSF valueChangeListener fired before UpdateModel phase

The other day I was trying to use a JSF valueChangeListener to compare 2 passwords fields for equality.

The bean code.

1
2
3
4
5
6
7
8
9
10
@In
private Map<String, UIComponent> uiComponent;

//Change listener to make sure passwords match
public void verifyPasswordMatches(ValueChangeEvent e) {
  verifyPassword = (String) e.getNewValue();
  UIComponent password = uiComponent.getSubmittedValue("registration:passwordField:password");
 
  //Compare the 2 fields.
}

The 2 fields in the view.

1
2
<h:inputSecret value="#{register.password} id="password" />
<h:inputSecret value="#{register.verifyPassword} valueChangeListener="{register.verifyPasswordMatches}" id="verifyPassword"/>

No matter what I tried I couldn’t get the value from the password field. After scratching my head for a while I finally figured out why. It is because the valueChangeListener is fired before the UpdateModel phase. This means the model is still empty which explains why the password field was blank. The only field that has a value is the field that has the valueChangeListener.

The morale of the story…remember the JSF life cycle.



Using Seam s:validateEquality to compare two fields for equality

I sometimes forget about some of the built in Seam Converters and Validators. While working on a project recently I had a requirement that had two password fields. This was to ensure that the password was spelled correctly. Pretty standard stuff when registering a new user. When I got to this requirement I started to roll my own validator and then remembered Seam had already done this for me. Here is the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<s:decorate id="passwordField" template="layout/new.xhtml">
  <ui:define name="label">Password:</ui:define>
  <h:inputSecret id="password" value="#{user.password}" required="true" immediate="true" redisplay="true">
    <a:support event="onblur" bypassUpdates="true" ajaxSingle="true" reRender="passwordField"/>
 </h:inputSecret>
</s:decorate>
 
<s:decorate id="verifyPasswordField" template="layout/new.xhtml">
  <ui:define name="label">Verify Password:</ui:define>
  <h:inputSecret id="verifyPassword" value="#{register.verifyPassword}" required="true" redisplay="true">
    <a:support event="onblur" bypassUpdates="true" ajaxSingle="true" reRender="verifyPasswordField"/>
    <s:validateEquality for=":#{rich:clientId('password')}" message="Passwords don't match." />
  </h:inputSecret>
</s:decorate>

That is all there is to it. Pretty slick. Now when the user tabs or clicks out of the Verify Password field the two fields are compared for equality and if they aren’t equal a validation error is displayed so the user can be made aware that there was a spelling mistake.

One thing to keep in mind is that there is a bug in this validator and the way it finds the id. In the Seam documentation example it shows the following.

1
<s:validateEquality for ="password"/>

When I tried this it didn’t work.

After some digging around in the Seam forums I found a post explaining that this was a bug and needed to be changed to the following.

1
<s:validateEquality for=":#{rich:clientId('password')}/>

.
The s:validateEquality can not only check for equality but it can also check for not equal, greater than, greater than or equal to, less than, and less than or equal to. Check out the Seam documentation for the complete listing of attributes.