selectOneMenu ajax events - ajax

I am using an editable primefaces selectOneMenu to display some values. If the user selects an item from the List a textarea should be updated. However, if the user types something in the selectOneMenu, the textarea should not be updated.
I thought I could work this with ajax event out. However, I don't know which event I can use here. I only know the valueChange event. Are there any other events, like onSelect or onKeyUp?
Here is my code:
<p:selectOneMenu id="betreff" style="width: 470px !important;"
editable="true" value="#{post.aktNachricht.subject}">
<p:ajax event="valueChange" update="msgtext"
listener="#{post.subjectSelectionChanged}" />
<f:selectItems value="#{post.subjectList}" />
</p:selectOneMenu>
<p:inputTextarea style="width:550px;" rows="15" id="msgtext"
value="#{post.aktNachricht.text}" />

The PrimeFaces ajax events sometimes are very poorly documented, so in most cases you must go to the source code and check yourself.
p:selectOneMenu supports change event:
<p:selectOneMenu ..>
<p:ajax event="change" update="msgtext"
listener="#{post.subjectSelectionChanged}" />
<!--...-->
</p:selectOneMenu>
which triggers listener with AjaxBehaviorEvent as argument in signature:
public void subjectSelectionChanged(final AjaxBehaviorEvent event) {...}

I'd rather use more convenient itemSelect event. With this event you can use org.primefaces.event.SelectEvent objects in your listener.
<p:selectOneMenu ...>
<p:ajax event="itemSelect"
update="messages"
listener="#{beanMB.onItemSelectedListener}"/>
</p:selectOneMenu>
With such listener:
public void onItemSelectedListener(SelectEvent event){
MyItem selectedItem = (MyItem) event.getObject();
//do something with selected value
}

Be carefull that the page does not contain any empty component which has "required" attribute as "true" before your selectOneMenu component running.
If you use a component such as
<p:inputText label="Nm:" id="id_name" value="#{ myHelper.name}" required="true"/>
then,
<p:selectOneMenu .....></p:selectOneMenu>
and forget to fill the required component, ajax listener of selectoneMenu cannot be executed.

You could check whether the value of your selectOneMenu component belongs to the list of subjects.
Namely:
public void subjectSelectionChanged() {
// Cancel if subject is manually written
if (!subjectList.contains(aktNachricht.subject)) { return; }
// Write your code here in case the user selected (or wrote) an item of the list
// ....
}
Supposedly subjectList is a collection type, like ArrayList. Of course here your code will run in case the user writes an item of your selectOneMenu list.

Related

JSF form validation skip specific validator for second submit when user wants to override

I am validating a user entered account number using two validators, one for basic standard format, and the other that validates the account number against values stored in a database. The database of valid account numbers may not always be up to date so I want to allow the user to override and submit their entered account number but only after the database validation has failed. I always want to validate its standard format 8 characters with no spaces.
<h:form id="formId">
<p:panelGrid>
<p:row>
<p:column>
<p:outputLabel value="Account : " for="acct" />
</p:column>
<p:column>
<p:selectOneMenu id="acct" value="#{bean.acct.acctNum}" effect="fold" editable="true" validator="acctLengthAndSpaceValidator" required="true" requiredMessage="Required">
<f:selectItems value="#{bean.mySavedAccounts}" var="acct"
itemLabel="#{acct.acctNumber} itemValue="#{acct.acctNumber}" />
<o:validator validatorId="accountDatabaseValidator" disabled="#{bean.skipDbValidation}" />
</p:selectOneMenu>
</p:column>
<p:column>
<p:messages for="acct" showDetail="true" skipDetailIfEqualsSummary="true" />
</p:column>
</p:row>
</p:panelGrid>
<br />
<p:selectBooleanCheckbox rendered="#{facesContext.validationFailed}" value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" listener="#{bean.testListener()}" />
</p:selectBooleanCheckbox>
<p:commandButton value="Submit" action="#{bean.submit()}" update="formId"/>
</h:form>
The checkbox does appear after the form is initially submitted and has any validation failure (I will figure out how to isolate to just the failed accountDatabaseValidator). But then when I select the checkbox, and submit again, both validators are still fired. I added the ajax listener to debug, and it isn't firing and the boolean value skipDbValidation is still false.
Perhaps my approach is not correct in achieving my concrete goal of validating against the database but then giving the user the option of skipping the db validation after initial failure.
EDIT
if i remove rendered="#{facesContext.validationFailed}" from the checkbox and have it visible all the time, the boolean skipDbValidation will get set to true if the checkbox is checked and then on subsequent submit, the skipDbValidation is ignored as expected. But I do not want the checkbox allowing the user to bypass visible at first. Only after validation fails.
The technical explanation that this doesn't work is that the rendered attribute is re-evaluated during processing the form submit. At this point the faces context is not validationFailed anymore (it was only validationFailed during the previous request) and thus the component is not rendered anymore and then the component's submitted value won't be applied. This matches #6 of commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated.
Your work around by rendering it client-side rather than server-side is acceptable. But I gather that you wanted to show it only when the specific validator has been invoked. There are at least 2 ways to achieve this:
Check UIInput#isValid() of the input component of interest. You can achieve that by binding the component to the view (not to the bean!) via component's binding attribute so you can reference it elsewhere in the same view.
<p:selectOneMenu binding="#{acct}" ...>
...
</p:selectOneMenu>
...
<p:selectBooleanCheckbox styleClass="#{acct.valid ? 'ui-helper-hidden' : ''}" ...>
...
</p:selectBooleanCheckbox>
Note that I took the opportunity to reuse the PrimeFaces-provided style class.
Or, make the validator a view scoped bean and reference it via <o:validator binding> instead.
#Named
#ViewScoped
public class AccountDatabaseValidator implements Validator, Serializable {
private boolean validationFailed;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
validationFailed = !isValid(value);
if (validationFailed) {
throw new ValidatorException(createError("Invalid value"));
}
}
public boolean isValidationFailed() {
return validationFailed;
}
}
<p:selectOneMenu ...>
<o:validator binding="#{accountDatabaseValidator}" ... />
</p:selectOneMenu>
...
<p:selectBooleanCheckbox rendered="#{accountDatabaseValidator.validationFailed}" ...>
...
</p:selectBooleanCheckbox>
My work around to get the checkbox to programmatically display and so the checkbox would function was to hide and display using CSS instead of the render attribute.
style="#{facesContext.validationFailed ? 'Display: inline' : 'Display: none;'}"
<p:selectBooleanCheckbox style="#{facesContext.validationFailed ? 'Display: inline' : 'Display: none;'}" value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" />
</p:selectBooleanCheckbox>
But I still can't figure out how to display the checkbox for a specific validation failure.
I will post another question for that
EDIT
Here is how I ended up displaying the checkbox only after the Invalid Account validation failure.
<p:selectBooleanCheckbox style="#{facesContext.messageList.stream()
.anyMatch(v -> v.summary == 'Invalid Account') or
bean.skipDbValidation ? 'Display: inline' : 'Display: none;'}"
value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" />
</p:selectBooleanCheckbox>

Hide and show components depending on h:selectOneRadio value

I have a page with a p:selectOneRadio and I want to show one datatable depending on the chosen value of the radio selection. I have the error:
GRAVE: javax.el.MethodNotFoundException: .......changeListenerMethod(javax.faces.event.AjaxBehaviourEvent)
My code is the following:
<p:selectOneRadio value="#{analysisOrderForm.selectedOrderDomain}">
<f:selectItem itemLabel="choice1" itemValue="choice1"></f:selectItem>
<f:selectItem itemLabel="choice2" itemValue="choice2"></f:selectItem>
<p:ajax event="change" listener="#{analysisOrderForm.changeListenerMethod}"/>
</p:selectOneRadio>
<h:PanelGroup>
<p:dataTable rendered="#{analysisOrderForm.selectedOrderDomain == 'choice1'}">....</p:dataTable>
<p:dataTable rendered="#{analysisOrderForm.selectedOrderDomain == 'choice2'}">....</p:dataTable>
</h:PanelGroup>
The code of my 'changeListenerMethod method' is just:
public void changeListenerMethod(ValueChangeEvent e){
setSelectedOrderDomain(e.getValue().toString());
}
What is correct and what is wrong in my code?
When using
<p:ajax event="change" listener="#{analysisOrderForm.changeListenerMethod}"/>
JSF will look for a method in your analysisOrderForm bean which has the following signature: public void changeListenerMethod(AjaxBehaviourEvent e). So you just need to replace ValueChangeEvent by AjaxBehaviourEvent.
Notes:
Another alternative approach is to write a method that doesn't take any arguments such as public void changeListenerMethod() and references it in your listener with listener="#{analysisOrderForm.changeListenerMethod()}
Event change is not the right event in your case. p:selectOneRadio event should be click (besides, that's the default event value). See also https://stackoverflow.com/a/21292158/2118909

Execute Backing Bean Action on p:selectOneRadio

I'm using a radio component on which, when selecting an item I want to execute an action on backing bean (not navigate to an outcome page, but to perform some action and then update current page via Ajax). Problem is I can't get the backing bean value change listener action to execute
<h:form id="one-radio">
<p:selectOneRadio layout="grid"
valueChangeListener="#{myBean.selectRadioItem}" >
<p:ajax process="#this" update="#form" />
<f:selectItems value="#{myBean.radioOptions}" var="radio"
itemLabel="#{radio.name}" itemValue="#{radio.id}" >
</f:selectItems>
</p:selectOneRadio>
</h:form>
and the backing bean method...
public void selectRadioItem(ValueChangeEvent event) {
String[] selectedItems = (String[]) event.getNewValue();
//more...
}
is there something wrong in the code which I'm missing? I've used this same structure for select many check box which is working...
Rodrigo, there's a difference between valueChangeListener and a simple method call via ajax.
Check this answer by BalusC about the difference between valueChangeListener and <f:ajax>
To solve your problem, you could use the listener property of <p:ajax>.
OneRadio component is used to receive only one value, if you want to select a list of values, use SelectOneMenu
Try to do the following:
<p:selectOneRadio layout="grid" value="#{myBean.radioValue}">
<p:ajax process="#this" update="#form" listener="#{myBean.selectRadioItem}"/>
<f:selectItems value="#{myBean.radioOptions}" var="radio"
itemLabel="#{radio.name}" itemValue="#{radio.id}" >
</f:selectItems>
</p:selectOneRadio>
In the backbean, you can remove the event parameter, because the value of the OneRadio component now is a property, which I named as radioValue
String radioValue;
...
public void selectRadioItem() {
String selectedItem = this.radioValue;
//more...
}

SelectOneMenu set to null when updating p:panelGrid

I am having a problem, I am firing a ajax event in a selectOneMenu like this: the first one does not fire any event
<p:selectOneMenu id="IdSelectOne" value="#{MB.myentity.myValue}" converter="myConverter1">
.....
</p:selectOneMenu>
<p:selectOneMenu id="IdSelectTwo" converter="myConverter2">
<p:ajax event="change" process="#this" partialSubmit="true"
listener="#{MB.ChangeOption}" update="creatPanel" >
</p:ajax>
</p:selectOneMenu>
The problem is that when the ajax event updates the panel, the first SelectOneMenu sets tu null, i can't use #form because it validates all fields and never do what I need which is show a field based on the selection of the second SelectOneMenu, is there a way to avoid this problem to happend?
This is my backing bean:
public void ChangeOption(AjaxBehaviorEvent event){
.....
}
I solved it by updating just the element I needed and not the complete panel, Thanks

Updating a checkbox from a radio button

I am trying to add ajax behavior to selectoneradio with this code:
xhtml:
<h:selectOneRadio id="metalA" converter="metalConverter" value="#{backingBean.metal.metalCode">
<f:selectItems value="#{backingBean.metalCodeRadio}" />
<f:ajax listener="#{backingBean.updateMenu}" event="click" execute="metalA" render="metalTypeMenuA"/>
</h:selectOneRadio>
<p:outputPanel id="panelA">
<h:selectOneMenu id="metalTypeMenuA" converter="metalConverter" value="#{backingBean.order.metal}" rendered="#{teklifIslemleriBean.selectedITip == 1}">
<f:selectItems value="#{backingBean.metalDetailsMenu}" />
</h:selectOneMenu>
</p:outputPanel>
backing bean:
MetalCode selectedMK = null;
public void updateMenu(AjaxBehaviorEvent event) {
System.out.println("Entered to updateMenu method");
if (metal.getMetalKod()!= null) {
electedMK = aMetal.getMetalCode();
}
if (selectedMK != null) {
// metalTypeMenuA Combobox
List<Metal> metalList = aService.getAccToMetalCode(null, selectedMK);
System.out.println("MetalList:" + metalList.size());
metalTypeMenuA.clear();
for (Metal m : metalList) {
metalTypeMenuA.add(new SelectItem(m, "No:" + m.getMetalNo() + " ,Weight: " + m.getWeight();
}
}
}
However it does not even enter to the updateMenu method. instead of click I tried select, change, etc. I also tried to put a wrapper panel and update it instead of checkbox, still no good. What is wrong with above code? Is updating a checkbox with a change in radiobutton doable? Thanks in advance.
JSF 2.0 Primefaces 2.2.1
EDIT:
I added following
<h:message for="metalA" id="messaged"/>
<f:ajax listener="#{backingBean.updateMenu}" event="click" execute="metalKoduA" execute="metalA" render="messaged orderPG2"/>
orderPG2 is a wrapper around checkbox. But still I can get any error message in h:message or any ajax behavior is happening.
The render attribute of <f:ajax> should not point to a component which is by itself conditionally server-side rendered by rendered attribtue. Let it point to the closest parent which is always rendered instead.
<f:ajax listener="#{backingBean.updateMenu}" render="panelA" />
(note that I removed event="click" and execute="metalA" as those are the defaults already)
If that still doesn't work, then you'd need to read the server logs for any missing faces messages. Big change that you'll see a Validation Error: "Value is not valid" or perhaps a conversion error. To prevent those messages from being missed during ajax rendering, ensure that you're using <h:message> and/or <h:messages> the right way and that you also include them in the render of the <f:ajax>.
<h:selectOneRadio id="metalA" ...>
...
<f:ajax ... render="metalAmessage panelA" />
</h:selectOneRadio>
<h:message id="metalAmessage" for="metalA" />

Resources