How to display my application's errors in JSF? - 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!

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

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!

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.

Remember that:
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage( null, new FacesMessage( "The message to display in client" ));
is also valid, because when null is specified as first parameter, it is applied to the whole form.
More info: coreservlets.com //Outdated

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}" />

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.

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.

Simple answer, if you don't need to bind it to a specific component...
Java:
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Authentication failed", null);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, message);
XHTML:
<h:messages></h:messages>

Related

Form not submitting on a page with fragment 'rendered' conditions and ui:param

I'm creating a some sort of online shop with JSF. I have a product.xhtml page that displays the product by ID. I'm passing that ID as a param (.../product.xhtml?id=3) and I'm getting the ID with <ui:param name="productID" value="#{request.getParameter('id')}" />
That all works well.
Next, I'm showing and hiding certain elements in the page with <f:subview> (I've used <ui:fragment> before). The reason is that if the user deletes the ?id=3 ID parameter, the page will show an error (eg. code <f:subview id="main" rendered="#{productID != null and productID != ''}">). Another reason is that if the product belongs to the buyer, the BUY button will not appear and if the user is not authenticated the BUY button will not appear.
The problem is with the BUY button. It is in a form and the action of the button is just a simple test method (for now) from the CDI bean that prints something to the server console and redirects the user. Unfortunately, this does not work. The page (/product.xhtml) gets reloaded with no ID param.
I've tried several things like this and this and nothing is working.
I've tried using the ViewScoped and SessionScoped for my CDI bean instead of RequestScoped, but that does nothing. The ViewScoped fails to build.
I've also changed the <ui:fragment> to <f:subview>
Here's some code..
CDI bean controller
#Named
#RequestScoped
public class ProductManager {
...
public String buy(Product product) {
FacesContext context = FacesContext.getCurrentInstance();
try {
HttpSession session = Util.getSession();
User buyer = (User)session.getAttribute("user");
Date date = new Date();
System.out.println("TEST DATA: ");
System.out.println("sale product: "+product.getTitle());
System.out.println("sale buyer: "+buyer.getUsername());
System.out.println("sale date: "+date);
}
catch(Exception ex) {
System.err.println("ProductManager#buy -> "+ex.getMessage());
}
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "An error occured", null));
return "index";
}
...
}
Products.xhtml
...
<ui:param name="productID" value="#{request.getParameter('id')}" />
...
<f:subview id="buyBtn" rendered="#{user.username != login.username and login.regUser}">
<h:form style="margin-top: 30px;">
<b:navCommandLink styleClass="btn btn-info btn-block" disabled="#{!product.status}" value="Buy" action="#{productManager.buy(product)}"></b:navCommandLink>
</h:form>
</f:subview>
...
I can provide full code if needed.
What I'm expecting the code to do is that whenever I click on the BUY button, I'll get redirected to my page and the TEST DATA will be printed on server console.
After many. many attempts I solved this. I cannot find the exact explanation, as I read a ton of articles on this topic.
I used OmniFaces. Next in products.xhtml I changed h:form to o:form (from OmniFaces) and have these two set to true includeRequestParams="true" includeViewParams="true". My bean then became org.omnifaces.cdi.ViewScoped
This is also a great article

Primefaces won't call setter on ManagedBean

I'm in the middle of building a web application (Spring 4, Primefaces 5.1 and JPA).
While one of my xhtml-pages has no problems working with its ManagedBean, the others won't really work. To be more specific, the UI input components (like inputText) won't call any setters from the managed Beans.
I've already run my Tomcat (8) on debug mode, which showed me that the page calls the get-methods, but no set-methods at all. If I try to persist something, all values are null (or 0, in case of an int-value). (it even persists an object into the database with all values null, though I have declared some #NotNull constraints which should be taken to the database configuration via JPA)
So, question is: How can I make my inputFields work with the fields of my ManagedBean? (Eclipse also shows me in the editor, that theoretically, the connection to the fields is there, it knows the managed bean, the field and the get-/set-methods)
SoftwareManagedBean.java
#ManagedBean(name = "swmb")
#ViewScoped
public class SoftwareManagedBean extends AssetManagedBean implements
Serializable {
private String bezeichnung;
private Software neueSoftware;
// +some more private fields, every single one with its get-/set-method
#Override
public String getBezeichnung() {
return super.getBezeichnung();
}
#Override
public void setBezeichnung(final String bezeichnung) {
super.setBezeichnung(bezeichnung);
}
//instantiante the field "neueSoftware"
public void createEmptySoftware(){
if(neueSoftware != null)
return;
this.neueSoftware = new Software();
}
//Persist Software with values from inputFields
public void addSoftware() {
createEmptySoftware();
neueSoftware.setBezeichnung(getBezeichnung());
softwareService.addSoftware(neueSoftware);
//...
neueSoftware = null;
}
viewSoftware.xhtml
<h:body>
<p:dialog header="Neue Software anlegen" widgetVar="SwNeuDialog" width="60%"
closeOnEscape="true" draggable="false" resizable="false" position="center">
<h:form id="SwDlgForm">
<h:panelGrid columns="3" border="0" >
<p:outputLabel for="swBezeichnung" value="Bezeichnung: " />
<p:inputText id="swBezeichnung" value="#{swmb.bezeichnung}"
label="Bezeichnung" required="true" />
<f:verbatim/>
<p:outputLabel for="swKategorie" value="Kategorie: " />
<p:selectOneMenu id="swKategorie" value="#{swmb.kategorie}" label="Kategorie" required="true" >
<f:selectItem itemLabel="Kategorie wählen" value="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{swmb.kategorieListe}" var="kat" itemLabel="#{kat.bezeichnung}" itemValue="#{kat}"/>
</p:selectOneMenu>
<p:commandButton value="neue Kategorie hinzufügen" />
<!-- + some more input fields -->
<p:commandButton value="Speichern" action="#{swmb.addSoftware()}" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
<p:commandButton value="Abbrechen" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
</h:panelGrid>
</h:form>
</p:dialog>
<!-- dataTable -->
</h:body>
AssetManagedBean.java
#ManagedBean
public abstract class AssetManagedBean {
//name of the hard-/software
private String bezeichnung;
//+ some more fields with get-/set methods
public String getBezeichnung() {
return bezeichnung;
}
public void setBezeichnung(String bezeichnung) {
this.bezeichnung = bezeichnung;
}
I hope that code is sufficient to see the problem, since the rest of the code follows the same structure. I think, the problem could lie within the xhtml file, but I don't see where or why. I've got the SpringBeanFacesELResolver (or however it is called), I've already looked through the code and compared it to another xhtml page and its Managed Bean, but there are no differences anymore. (though one is working, one not)
My debugger showed, how the working class/page (viewAdministration.xhtml) called the get-/set methods of the managed bean:
opening dialog window: get...(), set...()
clicking the commandButton to submit/persist: get() (old Value), set() (new Value), get() (new Value)
Another get() (called by the add... method)
(+ another get() for the dataTable on the main page)
on viewSoftware.xhtml, it looks like this:
opening dialog window: get()
clicking the commandButton to submit/persist:
another get() called by the addSoftware method
As you can see, when I try to submit, there is no set or get.
So, to summarize:
no setter called by trying to submit
the code on viewSoftware.xhtml and SoftwareManagedBean is similar to another, functioning ManagedBean + xhtml page (I've compared it again and again)
annotations in Managed Beans are the same (#ManagedBean, #ViewScoped)
the inputFields are inside a form (
I'm totally clueless, but I think it is some small mistake from my side that I can't just see.
I've searched through the web and especially stackoverflow, but all the problems and answers I've found couldn't help me finding what's wrong
Even without inheriting from a superclass it won't work (tried that out too)
I hope, you can help me. If this post is lacking some information, I'm sorry about that, I tried my best to not let this post grow too big and still get as much relevant information in it as possible.
So, I have found my mistake (or at least, I think I have). Only took me 2 weeks, but anyway...
I tried to test it out more specifically, wrote test classes and an xhtml page. Nothing worked (from simple Input over Dates to own classes).
The solution to this problem was to disable ajax on my commandButton (ajax="false"). When I tried to understand more of it, I realized that the commandButton opening the dialog window was nested in a facet inside a dataTable, and thus it had problems to properly set the values of the input fields.
So, thank you for your help. Maybe/hopefully this can or will help some other people later as well.
From the first glance at the code, without reading it whole, try putting process="SwDlgForm" on your command buttons, instead of process="#this". If that doesn't solve the problem, I'll read more carefully and try to help.

How to evaluate a FacesComponent property inside a custom component

Lately, I've been working on a dynamic form project but is stop with a custom component problem. What I have:
A faces component for formField:
#FacesComponent(value = "formField")
public class FormFieldCompositeComponent {
private boolean email;
}
A custom component jsf:
<o:validator for="email_text" validatorId="emailValidator" disabled="#{not cc.email}" />
OR
<c:if test="#{not cc.email}">
<f:validator for="email_text" validatorId="emailValidator"></f:validator>
</c:if>
OR
<f:validator disabled="#{not cc.email}" for="email_text" validatorId="emailValidator"></f:validator>
And the validator:
#FacesValidator("emailValidator")
public class EmailValidator implements Validator { }
My problems are:
1.) If I use an ordinary f:validator, like the one I use above and then use c:if to enable/disable it, then it will not work. According to some articles I've read it's because f:validator validates on build time, not on render time.
2.) If I use o:validator, it works but the problem is every time you hit submit a new line of invalid email error is added to p:messages. Example I clicked submit button 3 times, then I get 3 times the email error.
Any idea?
More info (anatomy of the project)
Example I have a page user with field email, it will include the following custom components:
+user.xhtml
+formPanel
+formField (this is where the validator is defined)
+formButtons (the action button)
+p:messages is defined
user.xhtml
<formPanel>
<formField field="email" />
<formButtons />
</formPanel>
Command button is like (formButtons):
<p:commandButton id="saveButton" rendered="#{cc.attrs.edit}"
value="#{messages['action.save']}"
action="#{cc.attrs.backingBean.saveOrUpdate()}" icon="ui-icon-check"
ajax="#{cc.attrs.ajaxSubmit}">
<c:if test="#{cc.attrs.backingBean.lcid != null}">
<f:param name="cid" value="#{cc.attrs.backingBean.lcid}" />
</c:if>
</p:commandButton>
The p:messages as defined on formPanel:
<p:messages id="formMessages" showDetail="true" showSummary="false" redisplay="false"></p:messages>
Note:
1.) What I've noticed is that the validator is called n times, where n is the number of submit or click done.
xhtml - https://github.com/czetsuya/crud-faces/blob/master/crud-faces/src/main/webapp/administration/user/user.xhtml
the tags - https://github.com/czetsuya/crud-faces/tree/master/crud-faces/src/main/webapp/resources/tags
bean component - https://github.com/czetsuya/crud-faces/tree/master/crud-faces/src/main/java/org/manaty/view/composite
Seems like there's no chance for the f:validator so I push through o:validator and come up with a workaround. Need to catch if the error is already in the FacesMessages list:
boolean match = false;
for (FacesMessage fm : context.getMessageList()) {
if (fm.getDetail().equals(message)
|| fm.getSummary().equals(message)) {
match = true;
break;
}
}
if (!match) {
throw new ValidatorException(facesMessage);
}

How to set the exception message in a <h:message>?

I am trying to set an exception message in the <h:message>.
Here is the relevant view code:
<h:inputText id="titleId" value="#{bookController.book.title}"/>
<h:message for="titleId"/>
<h:commandButton value="Create a book" actionListener="#{bookController.doCreateBook}" action="listBooks"/>
I need a message to be displayed when the titleId is empty. My #Stateless EJB method throws an exception when the title is empty:
public Book createBook(Book book) throws CustomException {
if(book.getTitle().isEmpty()) {
throw new CustomException("Please, type a Title !");
}
else {
em.persist(book);
return book;
}
}
My backing bean catches it and sets a message:
public void doCreateBook() {
FacesContext ctx = FacesContext.getCurrentInstance();
try {
book = bookEJB.createBook(book);
bookList = bookEJB.findBooks();
} catch (CustomException e) {
ctx.addMessage("titleId", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", e.getMessage()));
}
}
What I except is, when the exception occurs, an error message must be displayed near the input text tag, but it isn't the case, the execution displays the page with list of books and the "Error" message displayed under the list, as shown below:
How can I get the full exception message to show up next to the input field?
Apart from the erroneous message handling which Thinksteep has already answered, your other mistake is that you're doing validation in an action method. This is not right. You should be using JSF builtin validation facilities instead. Whenever the JSF builtin validation fails, then the action method will not be invoked and the page will also not navigate. The enduser sticks to the current form and the message will appear in the therefor specified <h:message> tag.
In your particular case, you just need to set the required attribute.
<h:inputText id="titleId" value="#{bookController.book.title}" required="true" />
<h:message for="titleId" />
If you want to customize the default required message, use requiredMessage attribute.
<h:inputText id="titleId" value="#{bookController.book.title}"
required="true" requiredMessage="Please, type a Title !" />
<h:message for="titleId" />
Remove that input validation from the EJB method. It doesn't belong there. The EJB isn't responsible for that, the caller (which is in your case thus your JSF code) is responsible for that.
ctx.addMessage("titleId", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", e.getMessage()));
Your message text is Error and you are getting same. Change "Error" here to what ever you want.
PUT <h:messages showDetail="true" />

Issue with f:setPropertyActionListener and h:dataTable

I am trying to pass two parameters i.e. (nomComposantARejouer, typeFileARejouer) to an action method (gestionnaireMessagesController.rejouerMessage) using the setPropertyActionListener (we use jsf 1.2).
Here is the relevant jsp code:
<h:column>
<h:columnHeaderFacet>
<h:columnHeader value="#{msgs['pilotage.coordinateur.libelle.rejouer']}" />
</h:columnHeaderFacet>
<h:commandLink id="rejouer"
value="#{msgs['pilotage.coordinateur.libelle.rejouer']}"
action="#{gestionnaireMessagesController.rejouerMessage}">
<f:setPropertyActionListener
target="#{gestionnaireMessagesController.nomComposantARejouer}"
value="#{gestionnaireMessagesController.nomComposant}" />
<f:setPropertyActionListener
target="#{gestionnaireMessagesController.typeFileARejouer}"
value="#{gestionnaireMessagesController.typeFile}" />
</h:commandLink>
</h:column>
However, I always get a NPE because both parameters are null when used in the action method:
public String rejouerMessage() {
log.debug("-->"+nomComposantARejouer);//null
ParamResultatMessagesDTO message= (ParamResultatMessagesDTO) messagesTableau.getRowData();
log.debug("MessageId: " + message.getMessageId());
try {
Pager p = Pager.getInstance();
ParamRejouerMessageDTO prm = new ParamRejouerMessageDTO();
prm.setMessageId(message.getMessageId());
prm.setFileGet(nomsFilesMap.get(nomComposantARejouer).get(typeFileARejouer));
prm.setFilePut(nomsFilesMap.get(nomComposantARejouer).get("TASKQ"));
RejouerMessageService serv = (RejouerMessageService) this.getService(ServiceCst.REJOUER_MESSAGE_SERVICE);
serv.rejouerMessage(prm);
} catch (BusinessException e) {
this.addMessage(e);
} catch (ServiceException e) {
this.addMessage(e);
}
return chargerPage(); // TODO Navigation case.
}
I am not sure what I am getting wrong. Can anyone please help?
FYI, the variables nomComposant and typeFile can be displayed without problem and are not null.
Also ignore the h:columnHeaderFacet tags. They are inhouse tags that I've renamed.
You're basically doing:
gestionnaireMessagesController.setNomComposantARejouer(gestionnaireMessagesController.getNomComposant());
gestionnaireMessagesController.setTypeFileARejouer(gestionnaireMessagesController.getTypeFile());
during the invoke action phase of the form submit. Both properties are in the same bean instance and copied shortly before action method is invoked. This makes no sense. You seem to be expecting that the value is evaluated during the request of displaying the form. You seem to be thinking that <f:setPropertyActionListener> sets a request parameter. This is not true. The <f:param> is the only which does that.
So, this should do,
<f:param
name="nomComposantARejouer"
value="#{gestionnaireMessagesController.nomComposant}" />
<f:param
name="typeFileARejouer"
value="#{gestionnaireMessagesController.typeFile}" />
in combination with the following on <managed-bean> of gestionnaireMessagesController in faces-conig.xml:
<managed-property>
<property-name>nomComposantARejouer</property-name>
<value>#{param.nomComposantARejouer}</value>
</managed-property>
<managed-property>
<property-name>typeFileARejouer</property-name>
<value>#{param.typeFileARejouer}</value>
</managed-property>
(you can if necessary remove ARejouer so that it reuses the same property)
I have often encountered this problem , it occours when there are two f:setPropertyActionListener.
You try in this way:
<h:commandLink id="rejouer"
value="#{msgs['pilotage.coordinateur.libelle.rejouer']}"
action="#{gestionnaireMessagesController.rejouerMessage}"> <f:param
name="nomComposant"
value="#{gestionnaireMessagesController.nomComposant}" />
<f:param name="typeFile"
value="#{gestionnaireMessagesController.typeFile}" />
</h:commandLink>
Read this link: http://www.coderanch.com/t/211274/JSF/java/Passing-param-commandLink

Resources