Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display my application's errors in JSF?

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!

like image 842
Eric Noob Avatar asked Nov 24 '08 23:11

Eric Noob


2 Answers

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"       binding="#{showMessageAction.mybutton}"       action="#{showMessageAction.validatePassword}" />     <h:message for="mybutton" />   </h:form> </f:view> 

Managed bean logic:

/** Must be request scope for binding */ public class ShowMessageAction {      private UIComponent mybutton;      private boolean isOK = false;      public String validatePassword() {         if (isOK) {             return "ok";         }         else {             // invalid             FacesMessage message = new FacesMessage("Invalid password length");             FacesContext context = FacesContext.getCurrentInstance();             context.addMessage(mybutton.getClientId(context), message);         }         return null;     }      public void setMybutton(UIComponent mybutton) {         this.mybutton = mybutton;     }      public UIComponent getMybutton() {         return mybutton;     } } 
like image 72
McDowell Avatar answered Oct 13 '22 19:10

McDowell


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!

like image 33
Eric Noob Avatar answered Oct 13 '22 20:10

Eric Noob