views:

6627

answers:

6

In my JSF/Facelets app, here's a simplified version of part of my form:

<h:form id="myform">
  <h:inputSecret value="#{createNewPassword.newPassword1}" id="newPassword1" />
  <h:message class="error" for="newPassword1" />
  <h:inputSecret value="#{createNewPassword.newPassword2}" id="newPassword2" />
  <h:message class="error" for="newPassword2" />
  <h:commandButton value="Continue" action="#{createNewPassword.continueButton}" />
</h:form>

I'd like to be able to assign an error to a specific h:message tag based on something happening in the continueButton() method. Different errors need to be displayed for newPassword and newPassword2. A validator won't really work, because the method that will deliver results (from the DB) is run in the continueButton() method, and is too expensive to run twice.

I can't use the h:messages tag because the page has multiple places that I need to display different error messages. When I tried this, the page displayed duplicates of every message.

I tried this as a best guess, but no luck:

public Navigation continueButton() {
  ...
  expensiveMethod();
  if(...) {
    FacesContext.getCurrentInstance().addMessage("newPassword", new FacesMessage("Error: Your password is NOT strong enough."));
  }
}

What am I missing? Any help would be appreciated!

+1  A: 

Found this while Googling. The second post makes a point about the different phases of JSF, which might be causing your error message to become lost. Also, try null in place of "newPassword" because you do not have any object with the id newPassword.

daub815
I know that it's not getting lost because the the h:messages tag spits out the error just fine... I just don't know how to tie the error to a specific h:message tag. Changing the id didn't help either.
Eric Noob
Thanks for the FAQ link!
divideandconquer.se
A: 

JSF is a beast. I may be missing something, but I used to solve similar problems by saving the desired message to a property of the bean, and then displaying the property via an outputText:

<h:outputText
    value="#{CreateNewPasswordBean.errorMessage}"
    render="#{CreateNewPasswordBean.errorMessage != null}" />
Jan Zich
I can see how this would work, but there must be a more graceful way to handle error messages!
Eric Noob
I have to admit that I have resolved to the same thing when I have been unable to make friends with h:message(s) and rich:message(s)
divideandconquer.se
A: 

I tried this as a best guess, but no luck:

It looks right to me. Have you tried setting a message severity explicitly? Also I believe the ID needs to be the same as that of a component (i.e., you'd need to use newPassword1 or newPassword2, if those are your IDs, and not newPassword as you had in the example).

FacesContext.getCurrentInstance().addMessage("newPassword1", 
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Message"));

Then use <h:message for="newPassword1" /> to display the error message on the JSF page.

Phill Sacre
+1  A: 

Hi,

You also have to include the FormID in your call to addMessage().

 FacesContext.getCurrentInstance().addMessage("myform:newPassword1", new FacesMessage("Error: Your password is NOT strong enough."));

This should do the trick.

Regards.

huo73
+1  A: 

In case anyone was curious, I was able to figure this out based on all of your responses combined!

This is in the Facelet:

<h:form id="myform">
  <h:inputSecret value="#{createNewPassword.newPassword1}" id="newPassword1" />
  <h:message class="error" for="newPassword1" id="newPassword1Error" />
  <h:inputSecret value="#{createNewPassword.newPassword2}" id="newPassword2" />
  <h:message class="error" for="newPassword2" id="newPassword2Error" />
  <h:commandButton value="Continue" action="#{createNewPassword.continueButton}" />
</h:form>

This is in the continueButton() method:

FacesContext.getCurrentInstance().addMessage("myForm:newPassword1", new FacesMessage(PASSWORDS_DONT_MATCH, PASSWORDS_DONT_MATCH));

And it works! Thanks for the help!

Eric Noob
+3  A: 

FacesContext.addMessage(String, FacesMessage) requires the component's clientId, not it's id. If you're wondering why, think about having a control as a child of a dataTable, stamping out different values with the same control for each row - it would be possible to have a different message printed for each row. The id is always the same; the clientId is unique per row.

So "myform:mybutton" is the correct value, but hard-coding this is ill-advised. A lookup would create less coupling between the view and the business logic and would be an approach that works in more restrictive environments like portlets.

<f:view>
 <h:form>
  <h:commandButton id="mybutton" value="click"
                       action="#{showMessageAction.validatePassword}" />
  <h:message for="mybutton" />
 </h:form>
</f:view>

Managed bean logic:

public class ShowMessageAction {

    private boolean isOK = false;

    public String validatePassword() {
     if(isOK) {
      return "ok";
     } else {
      //invalid
      FacesMessage message = new FacesMessage("Invalid password length");
      FacesContext context = FacesContext.getCurrentInstance();
      UIComponent mybutton = findComponent(context.getViewRoot(), "mybutton");
      context.addMessage(mybutton.getClientId(context), message);
     }
     return null;
    }

    private UIComponent findComponent(UIComponent parent, String id) {
     if(id.equals(parent.getId())) {
      return parent;
     }
     Iterator<UIComponent> kids = parent.getFacetsAndChildren();
     while(kids.hasNext()) {
      UIComponent kid = kids.next();
      UIComponent found = findComponent(kid, id);
      if(found != null) {
       return found;
      }
     }
     return null;
    }

}
McDowell
Read the OP, the validator model can't be applied to this situation.
Eric Noob
Gah - should have been paying more attention.
McDowell
More stuff on using clientIds: http://illegalargumentexception.blogspot.com/2009/02/jsf-working-with-component-ids.html
McDowell
Thanks for the code to look up the client ID, that's helpful even if it doesn't answer the question ;o)
Jon
@Jon - note that there are times when that lookup code can fail - http://illegalargumentexception.blogspot.com/2009/10/jsf-working-with-component-identifiers.html#tuoloocid
McDowell