I have a simple JSF input form into edit page and Validator which checks the value for duplication:
<td>Name</td>
<td>
<h:outputText value="#{ud.name}"
rendered="#{not DCProfileTabGeneralController.editable}" />
<h:inputText id="dcname" value="#{ud.name}" rendered="#{DCProfileTabGeneralController.editable}"
validator="#{ValidatorDatacenterController.validateDatacenterName}" autocomplete="off">
<f:ajax event="blur" render="dcnamevalidator" />
</h:inputText>
<h:message id="dcnamevalidator" for="dcname" />
</td>
public void validateDatacenterName(FacesContext context, UIComponent component, Object value){
....
}
I'm interested is there any possible way to send a second value which will be used into the validation process?
<f:attribute> comes to mind. Add one to the inputtext and retrieve it in the validator.
<h:inputText id="dcname".....
<f:attribute name="param" value="myparam" />
</h:inputText>
And:
String param = (String) component.getAttributes().get("param");
Can get the value from EL. Good luck
You can add a postValidate event to validate multiple fields , like
<f:event listener="#{bean.validationMethod}" type="postValidate" />
this should fire before model updates and you can get the new value for different component with
FacesContext fc = FacesContext.getCurrentInstance();
UIComponent components = event.getComponent();
UIInput param1 = (UIInput) components.findComponent("param1");
UIInput param2 = (UIInput) components.findComponent("param2");
If the validation fails , call the FacesContext renderResponse method to skip model update.
Related
I have h:selectBooleanCheckbox but it passes 'false' to the validator. always.
<h:panelGroup id="tncPanel">
<label>
<h:selectBooleanCheckbox id="tncSelection" value="#{businessBean.tncSelection}">
<f:validator validatorId="checkboxValidator"/>
<f:attribute name="label" value="Please agree terms and conditions."/>
</h:selectBooleanCheckbox>
I have read and agreed to all the
<a href="#" data-toggle="modal" data-target="#modal-term-and-conditions">
Terms and Conditions.
</a>
</label>
</h:panelGroup>
<h:panelGroup id="buttonPanel">
<h:commandLink id="nextButton" action="#{businessBean.goNext}">
Submit
</h:commandLink>
</h:panelGroup>
Why I have panelGroup here is, based on logic in top of page, I have a logic to display/not the button and checkbox
This is my Validator.
#FacesValidator("checkboxValidator")
public class CheckboxValidator implements Validator {
private static final String DEFAULT_LABEL = "Please confirm the address.";
#Override
public void validate(FacesContext context, UIComponent component, Object value){
String checkBoxValue = String.valueOf(((HtmlSelectBooleanCheckbox) component).getSubmittedValue());
System.out.println("checkBoxValue " + checkBoxValue);
System.out.println("value2: " + value);
String label = String.valueOf(component.getAttributes().get("label"));
if ("null".equals(label)) {
label = DEFAULT_LABEL;
}
if("false".equalsIgnoreCase(checkBoxValue)){
FacesMessage msg = new FacesMessage(null, label);
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
Validator sysout always prints checkBoxValue false
UPDATE
After comment on Balusc, I add another sysout to print directly the parameter value. But still it's printing as value2: false.
There is a bug related to this situation , When you render a container that holds a from from another form you the rendered form loses its view state ,Thus form is submitted to no destination solution is to render the form container and the form its self from the other form
For example that won't work :
<h:panelGroup id="panel">
<h:form>
<h:commandButton value="Submit" rendered="#{not testAction.flag}">
<f:ajax render=":panel" listener="#{testAction.submit()}"/>
</h:commandButton>
</h:form>
<h:panelGroup id="panel">
<h:form id="secondForm" rendered="#{testAction.flag}">
<h:selectBooleanCheckbox id="checkBox" value="#{testAction.boolVal}">
<f:validator validatorId="checkboxValidator"/>
</h:selectBooleanCheckbox>
<h:commandButton value="Go" action="#{testAction.go()}"/>
</h:form>
</h:panelGroup>
Second form submit will be like this :
secondForm won't have its view state the form is submitted but with no destination on the server cuz the view object is not fetched on the server that requires the javax.faces.ViewState value to be fetched by what you have to do here is to render form as well in the f:ajax:
<f:ajax render=":panel :secondForm" listener="#{testAction.submit()}"/>
That inform server to backup the second form rendered with the javax.faces.ViewState
Refer to that post : Rendering other form by ajax causes its view state to be lost, how do I add this back? for more info
Hope that was the problem :)
i want update a bean property from the same jsf wo contains this bean property by getting the new value from a method inside this bean.
here is the jsf sample :
<h3>JSF Login Logout</h3>
<h:outputText value="Username" />
<h:inputText id="username" value="#{login.user}"></h:inputText>
<h:outputText value="Password" />
<h:inputSecret id="password" value="#{login.pwd}"></h:inputSecret>
<h:message for="password"></h:message>
<br /><br />
<h:commandButton action="#{login.validateUsernamePassword}"
value="Login"></h:commandButton>
here is the bean method :
//validate login
public String validateUsernamePassword() {
...
this.user = "admin";
...
}
i want return back to the same jsf and fill user field by the new value.
If you want to stay on the same page then you don't have to return String, just create void method. If you want to update fields after form submit you can do it in JSF component by ajax with render attribute and specify these fields with ID or by selectors like #form:
<h:commandButton action="#{login.validateUsernamePassword}" value="Login">
<f:ajax execute="formId" render="username otherComponentId"/>
</h:commandButton>
or update from backing bean:
public String validateUsernamePassword() {
...
this.user = "admin";
RequestContext.getCurrentInstance().update("formId:username"); //PrimeFaces solution since you have PrimeFaces tag on your question
...
}
Better option is to use render attribute and probably you would like to also update h:message for any occurred errors (give it ID and update it in render).
Difference between returning null and "" from a JSF action
Can I update a JSF component from a JSF backing bean method?
Render multiple components with f:ajax
I hope you can help me.
I work with jsf and richefaces, I have a jsf page and I want to include all other pages according to a parameter and my problem is how can I introduce the test condition in my xhtml file.
THanks for your response,
there is my code but it does not work,
my bean
public class TestBean {
private boolean ajoutH;
private boolean listH;
public boolean isListH() {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String,String> params =
fc.getExternalContext().getRequestParameterMap();
String id = params.get("listH");
if(id.equals("listH")){
return true;}
else{
return false;
}
}
public boolean isAjoutH() {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String,String> params =
fc.getExternalContext().getRequestParameterMap();
String id = params.get("ajoutH");
if(id.equals("ajoutH")){
return true;}
else{
return false;}
And my page jsf
<rich:panelMenuGroup label="Hotes">
<rich:panelMenuItem>
<h:outputText value="Ajouter Hote" event="onclick">
<f:param value="{ajoutH}" name="ajoutH" />
</h:outputText>
</rich:panelMenuItem>
<rich:panelMenuItem >
<h:outputText value="GĂ©rer Hotes" >
<f:param value="{listH}" name="listH" />
</h:outputText>
......
<h:panelGroup rendered="#{TestBean.ajoutH}">
<ui:include src="./ajoutHote.xhtml"/>
</h:panelGroup>
<h:panelGroup rendered="#{TestBean.listH}">
<ui:include src="./listHotes.xhtml"/>
</h:panelGroup>
There are different options here and since you're using richfaces I'll include an example for that one too.
If you go for a pure JSF approach, use an <h:panelGroup> with a rendered attribute on it, defined by your bean. This attribute must be a boolean.
<h:panelGroup rendered="#{yourBean.yourCondition}">
<ui:include src="pages/yourPage.xhtml">
...
</h:panelGroup>
Another option is to use an <a4j:region>. It works in the same way basically.
<a4j:region rendered="#{yourBean.yourCondition}">
<ui:include src="pages/yourPage.xhtml">
...
</a4j:region>
Regardless of your choice, your bean should define the condition:
public Boolean isYourCondition() {
return /*your logic here*/;
}
There are many approaches to take, just take your pick.
use a4j:outputpanel and inside it ui:include:
<a4j:outputPanel rendered="#{someBean.someBoolean}">
<ui:include src="somepage.xhtml" />
</a4j:outputPanel>
I want to change the inputTexts' values when I choose another Skin from my selectOneMenu.
Everything is doing well, my Converter gives back the right Object from the menu, but the inputTexts are not updated.
<h:form>
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
onchange="this.form.submit()" converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
</h:selectOneMenu>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}"></h:inputText>
<br />
<h:inputText id="tcolor" value="#{helloBean.currentSkin.titleBar.textColor}"></h:inputText>
<br />
<h:inputText id="bcolor" value="#{helloBean.currentSkin.titleBar.backgroundColorStart}"></h:inputText>
</h:form>
Here is what my Bean looks like. I debugged it and the Object currentSkin is set correctly. Now i need to know how to update the textfields content.
#ManagedBean
#SessionScoped
public class HelloBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<ExtendedSkin> mySkins;
private List<SelectItem> mySkinsSI;
private ExtendedSkin currentSkin;
public void skinValueChanged(ValueChangeEvent e) {
currentSkin = (ExtendedSkin) e.getNewValue();
FacesContext.getCurrentInstance().renderResponse();
}
public List<ExtendedSkin> getMySkins() {
mySkins = XMLParser.readExtendedSkins();
return mySkins;
}
public List<SelectItem> getMySkinsSI() {
mySkinsSI = new LinkedList<SelectItem>();
for (ExtendedSkin s : getMySkins()) {
mySkinsSI.add(new SelectItem(s, s.getTitle()));
}
return mySkinsSI;
}
public void setMySkinsSI(List<SelectItem> myItems) {
this.mySkinsSI = myItems;
}
public ExtendedSkin getCurrentSkin() {
if (currentSkin == null) {
currentSkin = getMySkins().get(0);
}
return currentSkin;
}
public void setCurrentSkin(ExtendedSkin currentSkin) {
this.currentSkin = currentSkin;
}
}
The problem here is that the converter is doing its work filling the helloBean.currentSkin object, but the values in the <h:inputText> that are bounded to this helloBean.currentSkin: title, textColor and backgroundColorStart will be send to the server and replace the actual values that were loaded by the converter. In other words:
The converter is executed and builds the helloBean.currentSkin based on the selected value.
The <h:inputText id="name"> empty value is sent to server and will be injected in helloBean.currentSkin.title. Same behavior for the other 2 <h:inputText>s.
The view will be loaded using the selected helloBean.currentSkin and it will load the helloBean.currentSkin.title with the empty value. Same behavior for the other 2 <h:inputText>s.
There are two possible solutions to this problem:
Move the <h:inputText>s outside the form, so the empty values won't be send to the server. When loading the view, it will maintain the values loaded in the converter.
<h:form>
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
onchange="this.form.submit()" converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
</h:selectOneMenu>
</h:form>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}"></h:inputText>
<!-- rest of Facelets code... -->
Since you're loading the helloBean.currentSkin while changing the selected value on your dropdownlist, you can add ajax behavior using <f:ajax> tag component inside the <h:selectOneMenu> and update the fields in a cleaner way. I would opt for this solution.
<h:form>
<!-- Note that there's no need of the onchange JavaScript function -->
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
<f:ajax process="#this" render="name tcolor bcolor" />
</h:selectOneMenu>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}" />
<h:inputText id="tcolor" value="#{helloBean.currentSkin.titleBar.textColor}" />
<br />
<h:inputText id="bcolor"
value="#{helloBean.currentSkin.titleBar.backgroundColorStart}" />
</h:form>
You can learn more about <f:ajax> in online tutorial like this one.
Since you're going to use an ajax call in your page, you should change your managed bean scope from #SessionScoped to #ViewScoped. More info about this here: Communication in JSF 2
Here is the scenario (simplified):
There is a bean (call it mrBean) with a member and the appropriate getters/setters:
private List<String> rootContext;
public void addContextItem() {
rootContext.add("");
}
The JSF code:
<h:form id="a_form">
<ui:repeat value="#{mrBean.stringList}" var="stringItem">
<h:inputText value="#{stringItem}" />
</ui:repeat>
<h:commandButton value="Add" action="#{mrBean.addContextItem}">
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
</h:form>
The problem is, when clicking the "Add" button, the values that were entered in the <h:inputText/> that represent the Strings in the stringList aren't executed.
Actually, the mrBean.stringList setter (setStringList(List<String> stringList)) is never called.
Any idea why?
Some info -
I'm using MyFaces JSF 2.0 on Tomcat 6.
The String class is immutable and doesn't have a setter for the value. The getter is basically the Object#toString() method.
You need to get/set the value directly on the List instead. You can do that by the list index which is available by <ui:repeat varStatus>.
<ui:repeat value="#{mrBean.stringList}" varStatus="loop">
<h:inputText value="#{mrBean.stringList[loop.index]}" />
</ui:repeat>
You don't need a setter for the stringList either. EL will get the item by List#get(index) and set the item by List#add(index,item).