I want to apply style to my validation messages because as of right now they are by default red and apply themselves on the bottom left of the page. For instance when a user types in an empty username field, the message "username is required" appears, but it has a bullet before it and it and is red. I also have custom validators on some of my input fields.
<h:panelGrid class="grid" columns="2" id="regPanel">
<h:form>
<h:outputLabel class="outputLabel" value="Username"/>
<h:inputText class="inputText" id="userNameInputText"
value="#{regBean.userName}" required="true"
requiredMessage="Username is required" >
</h:inputText> .... more form inputs with required fields
</h:form>
</h:panelGrid>
First of all, you have to put h:messages or h:message tags in your view where you want to have the messages rendered. With h:messages, all the message of the page are rendered in a central list. With h:message you can just render the messages connected to one component like this:
<h:inputText id="long" value="#{bean.longValue}"/>
<h:message for="long"/>
The styling can be changed with various attributes on h:messages and h:message. There are attributes to set the style directly or a style class for each message severity (info, warn, error, fatal). The style can be set like this:
<h:message for="long" warnStyle="color: green" infoStyle="color: blue"
errorClass="errorMsg" fatalClass="fatalMsg"/>
There is not standard way for "re-use" of the style classes. You would have to put them on each tag without modifications. But there are several ways this could be achieved:
Build a composite component for messages with default styling
Replace the renderer of the message component to support default styles
Use a component library like PrimeFaces
You can use message, growl of Primefaces. and you can get css class of validation message and override it.
For example: i changed css of growl message:
<style type="text/css">
.ui-growl-image.ui-growl-image-error{
background-image: none !important;
background-color: navy;
}
</style>
Demo:http://www.primefaces.org/showcase/ui/messages.jsf
If you use default message of JSF, you must add css class to each component or create your own component.
Related
I have two grids, and one tabView which contains 2 tabs, for first tab I have to show panelgrid1, and for tab2 = panelgrid2. I have used rendered attribute for both panels, and used tabchange event in tabView, this listener updates the status attribute in java, but in xhtml, same grid is still shown and doesn't change 2nd panelgrid.
You need to make absolutely sure that you refer in ajax update/render a component which is always rendered. It is not possible to refer a component which is by itself conditionally rendered in order to show/hide it.
<p:ajax ... update="foo" />
...
<h:panelGroup id="foo">
<h:panelGrid ... rendered="#{bean.grid == 1}">
...
</h:panelGrid>
<h:panelGrid ... rendered="#{bean.grid == 2}">
...
</h:panelGrid>
</h:panelGroup>
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
How can I use (eg ajax) to show a specific jsf element if I click the button where the ajax tag belongs to? But without having to use any backing bean, just by JSF components without JavaScript?
Is that possible?
Luiggi is right , You better use js for client side toggling
But ,
I think you can bind like this
<h:inputText id="first" binding="#{someViewButNonManagedBeanObject}"`...
and than
<h:inputText id="second" render="#{no empty someViewButNonManagedBeanObject}"`...
or
<h:inputText id="second" render="#{someViewButNonManagedBeanObject.value eq 'wow'}"`...
(Not tested)
h:messages component inside a h:form is showing messages related to outside components as well.
How can I restrict it to show messages only related to components inside the containing h:form?
I don't prefer bloating up my code with a separate h:message tag for each & every input component.
Using Primefaces 3.4-SNAPSHOT with JSF 2.2.0-m03
You can make an ajax call to only render the wanted <h:messages.
for example
<h:commandButton value="submit" actionListener="#{fooBean.submit}">
<f:ajax render="msgs"/>
</h:commandButton>
<h:messages id="msgs" globalOnly="true"/>
or in case of primefaces use update attribute in the command button to make the ajax call
I'm building a form that let the user enter database connection parameters. Once the parameters are typed
the user can test (btnTester) if connection can be established with its parameters.
In all cases, a message is produced for the user. Here the example of a failed connection attempt from backing bean code :
(...)
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Connection failed.", t.getLocalizedMessage()));
(...)
Here is the form code. I'd like the message to appear in a p:message. Unfortunately, nothing happen. No message (connection successful or not) is displayed after the button :\
Even with the global attribute set to false or true.
<h:form prependId="false">
(...)
<h:panelGrid>
<!-- Other form components here -->
<f:facet name="footer">
<p:commandButton
id="btnTester"
value="Tester"
actionListener="#{assistantCreationSourcesBean.testerConnexionBase}"
update="msgTester" />
<p:message id="msgTester" for="btnTester" />
</f:facet>
</h:panelGrid>
</h:form>
What am I missing ?
You are missing an update attribute on your <p:commandButton> that specifies the ID of the <p:message> component to update.
You should give the message component and ID and specify it in update of the commandButton.
I think your problem is here:
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Connection failed.", t.getLocalizedMessage()));
I'm assuming that you're calling FacesContext.addMessage(). That first parameter is the component id string. If you set it to null, then you force the message to be a global message. The <p:message> component you have defined is set to for="btnTester" so it will only display messages with a component id that matches the id of your btnTester component.
Except from the Javadoc for addMessage():
Append a FacesMessage to the set of messages associated with the specified client identifier, if clientId is not null. If clientId is null, this FacesMessage is assumed to not be associated with any specific component instance.
Link to FacesMessage Javadoc for addMessage() method
I use a growl instead of a message for solving my problem.
Found a solution that works Perfectly with message in Prime face.
Thought Sharing with you even if u worked it out with growl
Here is the solution
The add addMessage has 2 parameters, a UIComponent Client Id and FacesMessage. The facesMessage shows or defines the message and Component Child Id shows where the component is.
Here you have missed the Component Part.
In-order to make it work you should bind the UIComponent with the bean class.
So that it can find the Component .
Now do as follows,
Define a UIComponent in bean class
Bind it with the corresponding command Button
and get the Id From the component in the bean Class
Your Code must be changed like this
> private UIComponent component;
(...)
addMessage(component.getClientId(), new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Connection failed.", t.getLocalizedMessage()));
(...)
Inside the xhtml file you must bind the UIComponent
<h:form prependId="false">
(...)
<h:panelGrid>
<!-- Other form components here -->
<f:facet name="footer">
<p:commandButton
id="btnTester"
value="Tester"
actionListener="#{assistantCreationSourcesBean.testerConnexionBase}"
update="msgTester"
binding="#{assistantCreationSourcesBean.component}"/>
<p:message id="msgTester" for="btnTester" />
</f:facet>
</h:panelGrid>
</h:form>
Now the message will work..!! For me it worked perfectly :)
Here "for" attribute won't work for p:message use p:messages instead.
<p:messages id="txtname" showIcon="false"
showDetail="true" showSummary="false"
redisplay="false" for="someKey" display="text"/>
My guess is that you try to update a component that not yet being render. Try this
Use Firefox Firebug and check the html source to see if component id msgTester exist. If it is not then it explain why your update wont work. You cant update something that it is not there. How to fix this, well you can create a wrapper for this, and update your wrapper. Your wrapper should be something that it is always there, like h:form. IF you dont want to update the whole form, then you can try <p:outputPanel id="wrapper">. so your code would look like this
<h:form>
...
<p:outputPanel id="wrapper">
<p:commandButton ... update="wrapper"/>
<p:message />
</p:outputPanel>
</h:form>
try this and let me know if it work.
I have encountered this problem recently with PrimeFaces 6.0.19.
It turns out that I must have to call FacesContext.getCurrentInstance().validationFailed() before I call the addMessage() method so the message can be properly shown.
i.e.
FacesContext.getCurrentInstance().validationFailed();
//if you set the client ID to be null, you should add globalOnly=true in <p:messages>
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Connection failed.", t.getLocalizedMessage()));
I will try to be as brief as possible, please stay with me here
"A.jsf" -> managed bean : bean
"#{bean.list}": will take us to B.jsf
<p:growl id="msgs" showDetail="true"/>
<h:form id="myform1" enctype="multipart/form-data">
<p:panel header="Upload" style="font-size: 11px;">
<h:panelGrid columns="2" cellpadding="10">
<h:outputLabel value="Drawing:" />
<p:fileUpload fileUploadListener="#{bean.handleFileUpload}" update="msgs" allowTypes="*.*;"/>
</h:panelGrid>
<p:commandButton ajax="false" immediate="true" id="back" value="Back" action="#{bean.list}"/>
<p:commandButton ajax="false" id="persist" value="Persist" action="#{bean.handleRevision}" />
</p:panel>
</h:form>
Then the handleFileUpload()
if(!upload){
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "You do not have permission to upload.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
...
"B.jsf" -> managed bean: bean2
...
<p:growl id="msgs" showDetail="true"/>
...
When I click upload, it give me a growl error message "You do not have permission to upload.", which is good. But then when I click "Back", which will take me to B.jsf, I see the growl message "You do not have permission to upload." again. What seem to be happening is as I click the "Back", I send other form request to upload, which then generated the same error message, which then being displayed at B.jsf. Is there a way to fix this, beside putting the "Back" button into an empty form, because now I have two buttons standing on top of each others, instead of side by side. I try to do this:
FacesContext.getCurrentInstance().addMessage("tom", msg);
hoping that it would send to component with id="tom", so then the growl with id=msgs, would not get load, but no luck. I try to turn the upload flag on when I click the Back button, but the web form get requested before the method that handle the back navigation get called.
It is not as brief as I want it to be, therefore I want apologize for it :D
beside putting the "Back" button into an empty form, because now I have two buttons standing on top of each others
The HTML <form> is by default a block element. HTML block elements are by default been placed in a new line. You actually want to make it an inline element. You can do this using display: inline; in CSS.
Back to the actual problem, it however surprises me that the fileUploadListener method is called in spite of the immediate="true" in the p:commandButton. I tried to reproduce this and I can confirm this. But I wouldn't expect it to happen. Normally the immediate="true" on a button is the solution to skip submitting of the "whole" form (at least, skip the UIInput components without this attribute). Further investigation learnt me that the p:fileUpload isn't an UIInput component at all and that the listener is fired during apply request values phase instead of validations or update model values phase. So this behaviour is fully predictable, but imo still an oversight in the design.
Since the p:fileUpload requires ajax="false" on the p:commandButton component, you can on the other hand also just remove it from the back button so that it fires an ajaxical request and hereby skips the fileUploadListener being called.
Actually, putting the button in a different form sounds like an excellent solution. The reason the buttons don't align any more is that the new starting <form> element starts on its own line. You should be able to prevent this by adding form { display: inline; } to your CSS file.
That said, if you have some leftover error messages that you want to get rid of, you can do this in the initializing method of your backing bean (if you have one). The following works peachily:
public void clearErrorMessages() {
//it may get messy to debug why messages are swallowed
logger.debug("clearing messages, coming from " + new Exception().getStackTrace()[1]);
Iterator iter = FacesContext.getCurrentInstance().getMessages();
while (iter.hasNext()) {
FacesMessage msg = (FacesMessage) iter.next();
logger.debug("clearing message: " + msg.getDetail());
iter.remove();
}
}
The disadvantage here is that any errors that occur between submitting the form and initializing the backing bean of the target page are also swallowed.