I have the following problem. I use a form when I provide only PIN. I have validator which checks if its a 4-digit number. Then the action on submit is set to the method which checks if the PIN exists in the database. If not it does message = "no PIN"; I used the message in the output label below the form. Previously it was null so there was no message there. Now it changes into "no PIN" but I have to clear it after clicking the submit button again because the error message doesn't disappear when you enter for example "12as" PIN and validator takes care of it. How should i implement such situation? Maybe using an output label in such situtation is a wrong idea?
You should not perform validation in action method. You should use a real validator.
Just implement the Validator interface accordingly. E.g.
#FacesValidator("pinValidator")
public class PinValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String pin = (String) value;
if (pin == null || pin.isEmpty()) {
return; // Let required="true" deal with it if necessary.
}
if (!pin.matches("\\d{4}")) {
throw new ValidatorException(new FacesMessage("PIN must be 4 digits"));
}
if (!somePinService.exists(pin)) {
throw new ValidatorException(new FacesMessage("PIN is unknown"));
}
}
}
Use it as follows:
<h:outputLabel for="pin" value="PIN" />
<h:inputText id="pin" value="#{bean.pin}" validator="pinValidator" />
<h:message for="pin" />
The faces message of the validator exception will end up in the <h:message> associated with the component on which the validator is been fired.
If you're using ajax to submit the form, don't forget to make sure that the message is also taken into account on ajax render.
Unrelated to the concrete problem, the JSF <h:outputLabel> generates a HTML <label> element which is intented to label a form element (e.g. <input>, <select>, etc). It's absolutely not intented to show an arbitrary piece of text such as a validation message. I recommend to put JSF aside for now and start learning basic HTML. This way you will understand better which JSF components to pick to get the desired HTML output.
You can use JSF message component outside a validator:
For a message for your input in your form:
<h:message for="PIN"/>
And at your managed bean you can add a FacesMessage using:
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN,"No pin summary message","No pin detail message");
FacesContext.getCurrentInstance().addMessage("PIN", message);
No need to use a outputLabel here.
Related
Silly question, but that's my situation. I am having editable PrimeFaces selectOneMenu where inputField has following restriction:
lower and upper limit of number typed in
predefined text allowed
when decimal number is being typed allow only 2 decimal numbers
All is good except the last one with decimal number restriction. What it means is that I can't type there 1.111 but only 1.11. Change event keyUp for selectOneMenu is sadly added to the tag select but not to input.
Any ideas how to solve?
This calls for a custom validator. Create one which checks for the predefined values, if no match is found, check the number format. Basic example:
#FacesValidator("myValidator")
public class MyValidator implements Validator
{
private List<String> predefinedValues = Arrays.asList("my", "list");
#Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String valueStr = (String) value;
// Check if value is predefined
if (predefinedValues.contains(valueStr)) {
return;
}
// If not predefined, check number format
if (! valueStr.matches("^\\d+(\\.\\d\\d?)?$")) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
// Check number limits...
}
}
The validator can be used in your XHTML as:
<p:selectOneMenu editable="true" ...>
...
<f:validator validatorId="myValidator" />
</p:selectOneMenu>
As an alternative you could use jQuery to find the input field and bind a keypress listener to it. See for example: Jquery: filter input on keypress. However, I would keep the validator in place. Users could paste text for example.
See also:
How to perform validation in JSF, how to create a custom validator in JSF
Regular expression for floating point numbers
This question may be more of the type "conceptual" or "I don't understand JSF".
My scenario:
I have a JSF Page (index.xhtml) where I use a p:accordionPanel (but I don't think it matters what component it is). What I want to do is to set the activeIndexes of it.
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}">
// bla bla...
</p:accordionPanel>
And the (simplified) method in the backing bean:
public String getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return activeSections;
}
Now this works just fine on a normal page load.
But if I click on a p:commandButton (with ajax=false) (or anything else which "sends" data back to the server I guess) - I get the following exception:
/WEB-INF/tags/normalTextSection.xhtml #8,112 activeIndex="#{myController.getActiveIndexesForSections(name)}": Illegal Syntax for Set Operation
// bla..
Caused by: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation
After some googling / reading the error message I found that I need a setter.
First of all: I don't want a setter - do I really need one or is there a way to tell JSF I don't want this "behavior".
Second I realized that it's not that "easy" to provide a setter, because my method has a parameter (so public void setActiveIndexesForSections(String name, String activeIndexes) or public void setActiveIndexesForSections(String name)won't work).
What I came up with in the end is:
Create a (generic) "Pseudo-Property-class":
// just a dummy class since the class is recreated at every request
public class Property<T> implements Serializable {
private T val;
public Property(T val) {
this.val= val;
}
public T getVal() {
return val;
}
//no need to do anyhting
public void setVal(T val) {
}
}
Change the bean method:
public Property<String> getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return new Property<String>(activeSections);
}
And call it from the index.xhtml:
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}">
// bla bla...
</p:accordionPanel>
This works but obviously is a ugly hack/workaround.
What is the proper way to handle a situation like this? Or is what I'm doing simply completely wrong?
The setter is needed to remember the active indexes as they were when the form is submitted. Basically, you need to bind it as a value expression (with a property), not as a method expression (like an action method), nor to an unmodifiable collection (like activeIndex="#{param.tab}"). Exactly like as with input values. Technically, you're indeed doing it "simply completely wrong" ;)
The requirement is however understood. Given that you're really not interested in the changed active indexes, and thus want to reset them to defaults on every form submit, then you can bypass it by storing the result as a request attribute with help of <c:set>. This way you will fool EL to set it in the request attribute map instead of the intented bean property.
<c:set var="activeIndex" value="#{myController.getActiveIndexesForSections('whatever')}" scope="request" />
<p:accordionPanel multiple="true" activeIndex="#{activeIndex}">
<!-- bla bla... -->
</p:accordionPanel>
Under the covers, it will basically do externalContext.getRequestMap().put("activeIndex", value) as setter operation, which will obviously just work.
Update: upon inspecting the source code of AccordionPanel component, I saw another workaround given the fact that the activeIndex won't be set when the rendered attribute evaluates false. So just alter the rendered attribute to behave exactly that: evaluate false during update model values phase (the 4th phase).
<p:accordionPanel multiple="true"
activeIndex="#{myController.getActiveIndexesForSections('whatever')}"
rendered="#{facesContext.currentPhaseId.ordinal ne 4}">
<!-- bla bla... -->
</p:accordionPanel>
I spent already more time as is good about some saving or updating issue in inputtext field, i go right to the point:
i have basic single input text with some basic attributtes
<h:inputText id="name" value="#{salesController.selectedSalesName}" />
here is a getter for inputText value
public String getSelectedSalesName(){
for(DealerListView dealer : dealerList){
if(dealer.getDealerId() == getSelectedDealerId()){
return dealer.getName();
}
}
return "";
}
nested in there i use ajax tag
<f:ajax event="change" render="name" listener="#{salesController.updateSelectedSalesName()}" />
a here is back bean method for updating a input text field
public void updateSelectedSalesName() {
DealerData dealDat = BeanFactory.getHotelDAOService(DealerData.class).findOne(selectedDealerId);
dealDat.setName(name);
BeanFactory.getHotelDAOService(DealerData.class).update(dealDat);
}
whole result of this is stack trace which say
value="#{salesController.selectedSalesName}": Property 'selectedSalesName' not writable on type sk.hotel.web.controller.SalesController
I know that something changes is need for that getter method but try some combinations without result which make corect update of value to database.
(I dont use any commands buttons for submit,update only response on pressing Enter in that inputText field.)
I want some guide how can be modified this save/update process whether on back-bean or jsf layout
or maybe someone solved similar situation already,and can share his solution.
Thanks all of you for advice posts.
Regards and nice day
First, add a field:
String selectedSalesName;
Add a setter and setter:
public String getSelectedSalesName() {
return selectedSalesName;
}
public void setSelectedSalesName(String selectedSalesName) {
this.selectedSalesName = selectedSalesName;
}
Add a ajaxListener(AjaxBehaviurEvent event) to create a new Dealer or Update current Dealer
public void ajaxListener(AjaxBehaviorEvent event) {
Dao dao = BeanFactory.getHotelDAOService(DealerData.class)
if (selectedDealerId == null) {
DealarData dealerData= new DealerData();
dealerDate.setName(getSelectedSalesName());
dao.add(dealerData);
setDealer(dealerData);
} else {
DealerData dealDat = dao.findOne(selectedDealerId);
dealDat.setName(name);
dao.update(dealDat);
}
}
A setter to the current dealer
int selectedDealerId;
public void setDealer(DealerData dealer) {
selectedDealerId = dealer.getId();
selectedSalesName = dealer.getName();
}
And the xhtml page:
<h:inputText value="#{salesController.selectedSalesName}" id="idSalesInput">
<a4j:ajax event="keyup" listener="#{salesController.ajaxListener}"
execute="idSalesInput"/>
</h:inputText>
Change "keyup" for the event you want to listen.
When you press a key, the listener is called, and the value of idSalesInput is submitted (the setSelectedSalesName() method is called here, for this reason you got the Property 'selectedSalesName' not writable exception),and the listener create or update a new DealerData.
Have a nice Day and sorry for my bad english!
Binding value in your inputText is two way, when it is rendered than getter is called to calculate value, when it is submited (like in your AJAX event) setter is called for that property to set value in your backing bean. So JSF tries to call setSelectedSalesName(String value). JSF can't write your property, which means can't call setter.
See also:
AJAX listener not being fired for inside
I have three radio buttons, an inputText and a submit button. I only want to validate the input text on submit when a certain radio is selected. So I have
<h:inputText validator="#{myBean.validateNumber}" ... />
And in my bean I have
public void validateNumber(FacesContext context, UIComponent component,
Object value) throws ValidatorException{
if(selectedRadio.equals("Some Value"){
validate(selectedText);
}
}
public void validate(String number){
if (number != null && !number.isEmpty()) {
try {
Integer.parseInt(number);
} catch (NumberFormatException ex) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error", "Not a number."));
}
} else {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error", "Value is required."));
}
}
The one thing that make this not work is that when I submit, validateNumber(...) runs before my setter method for the radio button setSelectedRadio(String selectedRadio). Therefore causing this statements
if(selectedRadio.equals("Some Value"){
validate(selectedText);
}
to not execute correctly. Any idea on how to get around this problem?
The selectedRadio is as being a model value only updated during update model values phase, which is after the validations phase. That's why it's still the initial model value while you're trying to examine it.
You'd have to grab it from either the request parameter map (which is the raw submitted value), or the UIInput reference, so that you can get either the submitted value by getSubmittedValue() or the converted/validated value by getValue().
So,
String selectedRadio = externalContext.getRequestParameterMap().get("formId:radioId");
or
UIInput radio = (UIInput) viewRoot.findComponent("formId:radioId"); // Could if necessary be passed as component attribute.
String submittedValue = radio.getSubmittedValue(); // Only if radio component is positioned after input text, otherwise it's null if successfully converted/validated.
// or
String convertedAndValidatedValue = radio.getValue(); // Only if radio component is positioned before input text, otherwise it's the initial model value.
It is called cross-field validation (validate not only based in the value of a component, but a set of them).
Currently, JSF2 does not support it ( JSF doesn't support cross-field validation, is there a workaround? ) but there are several libraries (in the refered question omnifaces is mentioned, it looks like seamfaces also has something for it) that might help. Also in the question there is a workaround.
JSF view code:
<f:view>
<h:form>
<h:panelGrid>
<h:inputText id="key" value="#{myManagedBean.key}"/>
<h:selectBooleanCheckbox id="rerun" value="#{myManagedBean.rerun}" rendered="#{myManagedBean.displayRerun}"/>
<h:commandButton id="check" action="#{myManagedBean.check}"/>
</h:panelGrid>
</h:form>
<f:view>
JSF model code:
public class MyManagedBean {
private boolean displayRerun;
public void setDisplayRerun(boolean aDisplayRerun) {
this.displayRerun = aDisplayRerun }
public boolean getDisplayRerun() {
return this.displayRerun;
}
private String key;
public void setKey(String aKey) {
this.key = aKey
}
public String getKey() {
return this.key;
}
private boolean rerun;
public void setRerun(boolean arerun) {
this.rerun = arerun
}
public boolean getRerun() {
return this.rerun;
}
public String check() {
//do data validation
setDisplayRerun(true);
System.out.println(getRerun());
}
}
This always prints false regardless of whether the checkbox is checked or not.
Additional Information on my requirement:
Nick/BalusC, my managed bean is of request scope. It is indeed simplified code snippet that I presented. My page has couple of user input controls along with a command button. On submit of command button, I call action method of backing bean, in which I do data validation (in this case I lookup database and see if the inputs are already registered.) If already registered, I come back to the same page, this is when I display the singleBooleanCheckBox for the user to select and hit the command button again.
I am toggling the display of the checkbox based on a managedbean property (a boolean flag set during data validation).
When I re-submit the page with checkbox checked, I do not receive this data.
For further verification, I replace the selectBooleanCheckbox, with a command button with similar behavior (basically do not render it initially, but only show it on data validation). I mapped its #action to my managedbean's action method. To my surprise, when I hit the button, the action method is not executed. Instead, the page is refreshed like in a "immediate" scenario or a redirect.
I have been struggling on this for almost 6 hrs. Appreciate your experienced insights.
Thanks.
So, you've actually a rendered attribute on the checkbox (that was not present in your initial question) and the bean is request scoped (it would have worked when it was session scoped). The submitted checkbox value will not be gathered during apply request values phase when this attribtue evaluates false at that point.
You basically need to retain the condition responsible for the rendered attribute in the subsequent request as well. This can be done in several ways: putting bean in session scope, using Tomahawk's t:saveState or h:inputHidden with a bean binding. Each is outlined in detail in this answer.