<h:selectOneMenu> value change listener invoked for all dropdowns instead of only the current - jsf

I'm using MyFaces 1.1. I have two <h:selectOneMenu>s dropdowns which each point to same valueChangeListener method.
<h:selectOneMenu id="d1" value="#{mybean.selectedChannel1}"
onchange="submit()" valueChangeListener="#{myform.channelValuechange}">
<f:selectItems value="#{mybean.channelList}"/>
</h:selectOneMenu>
<h:selectOneMenu id="d2" value="#{mybean.selectedChannel2}"
onchange="submit()" valueChangeListener="#{myform.channelValuechange}">
<f:selectItems value="#{mybean.channelList}"/>
</h:selectOneMenu>
When I change the first dropdown, then the value change listener method get fired correctly. In the method, I'm obtaining the ID of the current component as sourceId via ValueChangeEvent argument and then comparing it as follows:
if (sourceId.equals("d1")) {
// ...
} else if (sourceId.equals("d2")) {
// ...
}
However, my concrete problem is that d2 block is also called when d1 is changed.
I tried the one and other and figured that the following helped to solve the problem:
if (!event.getPhaseId().equals(PhaseId.INVOKE_APPLICATION)) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
}
However, I don't feel like that it's the best solution. How is this caused and how can I solve it without using the above code?

With onchange="submit()" you're basically submitting the entire form when the current input element is changed, not only the currently changed input! In contrary to what many starters incorrectly think, there is no means of any input-specific JavaScript/Ajax magic here. As you're submitting the entire form, it would trigger the processing of all input components.
The valueChangeListener is always invoked when the submitted value of the input component does not equals() the initial model value as in the backing bean. Given that in your case both menus hit the value change listener when you change only the first one, that can only mean that the default select item value of the second menu does not equals() the initial model value in the backing bean.
You need to make sure that #{mybean.selectedChannel2} of the second menu has by default exactly the same value as the first item of #{mybean.channelList} of the second menu's list. This way the value change listener won't be invoked for the second menu when you change the first menu.
See also:
When to use valueChangeListener or f:ajax listener? (just to learn what's different in JSF 2, for the case you're interested)

Related

JSF h:selectOneMenu wont call relevant setter method

Hi I've got a question
<h:selectOneMenu id="cmbFileStatus1" disabled="#{!schedulerController.oldFile}"
value="#{schedulerController.test}">
<f:selectItem itemValue="#{null}" itemLabel="--Show All--" noSelectionOption="true"/>
<f:selectItems value="#{schedulerController.statusList}"/>
<f:ajax execute="#this" render="dataTable"/>
</h:selectOneMenu>
The above code the ajax executes(I checked through firebug). But the thing is the selected value wont be set to h:selectOneMenu value parameter.
There is a h:form tag that is wrapping this element, plus there are two other elements similarly using ajax as shown here. But they are positioned before this element in the DOM, they call the relevant setter methods and the updates the bean variables.
But this element it doesn't set necessary selected value.
Also another detail, the list that is populated for selection, it is a list created from enum values.
One instance I moved the problematic code to the top of the DOM (before the other two elements that ajax is applied) and then it hit the setter method when ran in debug mode.
I cannot understand whats wrong, it doesn't show any javascript errors and such. The JSF version is 2.0, this is an old project.
Any ideas guys?

Rendering a data-table only when the bean returns a list

I have been trying to render a rich:dataTable, but fails, when it comes to its conditional rendering.I wanted to render it only if the size of the list, the backing-bean fetches from DB, is greater than zero.
JSF-2.0, RichFaces-4 are what i use.
You have to use the "render" attribute of the datatable. With it you can define if the component is rendered to the client or not. So check by EL if the list is populated.
you can do something like:
rendered="#{not empty listObject}"
and all is fine.
I always implement my database query method to never return null, if the query has no result I return an empty list. This way i'm sure I never get a nullpointerexception and I prefer then to show an empty table. Because it's easer to layout the page, when you are sure the table always exist.
Hope that helps.
The scenario is I have a groupId which I rightclick on. On the context menu, I choose Display CTNs and it should then render all the CTNs of this group in a data-table. It starts with a JavaScript call, which I call once I choose "Display CTNs". It takes care of supplying the GroupId to the a4j:jsFunction.
<rich:dataTable value="#{ctnGrpMgmtController.ctnDetailsList}"
var="ctnVar" id="ctnTable" rows="5"
rendered="#{not empty ctnDetailsList}">
The above should be rendered after the below a4j:jsFunction executes.
<a4j:jsFunction name="selectGroupForManagingCtns"
action="#{ctnGrpMgmtController.loadCTNsForAGroup}"
render="ctnListPanel,ctnTable">
<a4j:param name="name"
assignTo="#{ctnGrpMgmtController.groupId}" />
</a4j:jsFunction>
I have to do an F5 to see the component "ctnTable", which is where the problem starts.
It looks like the attribute name on the a4j:jsFunction is reRender instead of just render. Should fix it.

Valuechangelistener Doubt in JSF

HI,
Please see the following code:
<h:selectOneMenu id="countries" value="#{countryBean.selectedCountry}" onchange="submit()
valueChangeListener="#{countryBean.changeCountry}">
<f:selectItems value="#{countryBean.countries }" />
</h:selectOneMenu>
Backing Bean
public void changeCountry(ValueChangeEvent event){
String newValue = (String)event.getNewValue();
String oldValue = (String)event.getOldValue();
System.out.println("New Value : " + newValue);
System.out.println("Old Value : " + oldValue);
if ("1".equals(newValue)){
this.countries = new ArrayList<SelectItem>();
this.cities.add(new SelectItem("1","Delhi"));
this.cities.add(new SelectItem("2","Mumbai"));
}
if ("2".equals(newValue)){
this.cities = new ArrayList<SelectItem>();
this.cities.add(new SelectItem("1","Mossco"));
}
}
Please let me know if the implementation is correct. It is working fine.
My questions are:
What is the advantage of adding the f:valueChangeListener tag inside the h:selectOneMenu tag. I have used the normal attribute valueChangeListener="#{countryBean.changeCountry}".
Is it necessary to use onchange="submit() this code to change the values.
What is difference between writing the custom listeners by implementing the ActionListener interface and just using the attribute in the UIComponent tags (action="methodName").
Please explain me.
The ValueChangeListener will only be called when the form is submitted, not when the value of the input is changed. Thus, if you want to run this listener when the value is modified, you have two solutions:
Submit your form when the onchange event is fired (this is what you did in your code);
Use an Ajax call instead, by using some dedicated components (already integrated in JSF2, with <f:ajax>, or third-parties libraries such as Richfaces, Primefaces...).
Here is an example with Richfaces:
<h:selectOneMenu id="countries" value="#{countryBean.selectedCountry}" valueChangeListener="#{countryBean.changeCountry}">
<a4j:support event="onchange" .../>
<f:selectItems value="#{countryBean.countries }" />
</h:selectOneMenu>
Regarding the code of your listener, it seems correct, but why question is why do you need a ValueChangeListener here? Indeed, this listener is usefull when you want to track a modification of a value. That's why the ValueChangeEvent provides both getOldValue() and getNewValue() methods.
In your code, you do not care about the old value, so basically, you could "simply" do an action instead of a valueChangeListener (ex. with Richfaces):
<h:selectOneMenu id="countries" value="#{countryBean.selectedCountry}">
<a4j:support event="onchange" actionListener="#{countryBean.changeCountry}"/>
<f:selectItems value="#{countryBean.countries }" />
</h:selectOneMenu>
Finally, regarding the difference between the valueChangeListener attribute and <f:valueChangeListener> is that the first binds a Java method (#{myBean.myMethod}), while the second binds a Java class (type="com.foo.MyListenerClass") which implements the ValueChangeListener interface. So the second one could be more generic than the first one...
Romaintaz already pointed out the most, I just wanted to get straight on your concrete questions:
What is the advantage of adding the f:valueChangeListener tag inside the h:selectOneMenu tag. I have used the normal attribute valueChangeListener="#{countryBean.changeCountry}".
As Romaintaz said, the attribute points to a method and the f: tag points to a class. Another advantage is that you can have multiple of them, whenever that is necessary.
Is it necessary to use onchange="submit() this code to change the values.
That Javascript doesn't change the values. That Javascript submits the entire form without the need to pressing the submit button yourself, whenever the value has been changed by the enduser. No, that is not necessary. You can also just remove it and expect that the enduser presses the submit button himself. Once again, that JavaScript is not part of JSF.
What is difference between writing the custom listeners by implementing the ActionListener interface and just using the attribute in the UIComponent tags (action="methodName").
This question is already asked before: difference between action and actionlistener.
The solution from romaintaz of calling an action instead of valueChangeListener is also great because in the case of "change" event the action is called after the model is updated (allowing for a DB update for example) while the valueChangeListener is called before....

JSF selectOneMenu is refreshing and going back to its previous state rather than showing the new value

I have a datatable where a lot of selectOneMenu items are available , for example, for 10 items each having one selectOneMenu combo. now if i click on any of the combos, they are supposed to save the value in the database and they do it. but after saving the changed value the selectOneMenu is returning back to its previous state. I want the selectOneMenu to keep its current state. also, the method is being invoked for every single combo in the datatable. i really wonder why!! i have been banging my head for the last 2 weeks. any help would be really appreciated. thanks in advance.
this is my first post here. this is my jsf datatable:
<h:dataTable value="#{careNeedBean.controlledCareNeedsList}" var="careNeed"
id="careneed_table" binding="#{careNeedBean.dataTable}">
<h:column>
<f:facet name="header">
<h:outputText value="NeedsLevel"/>
</f:facet>
<h:selectOneMenu id="needs_level_combo" style="width:200px;font-size:9px;"
onchange="submit()"
valueChangeListener="#{careNeedBean.saveTaskAsessment}"
binding="#{careNeedBean.selectOneMenu}">
<f:selectItem itemValue="not_assessed" itemLabel="----Not assessed----"/>
<f:selectItems value="#{careNeed.humanReadableNeedsList}" />
</h:selectOneMenu>
</h:column>
This is my bean code:
public String saveTaskAsessment(ValueChangeEvent event) {
//does some things
return "Success";
}
The valueChangeListener doesn't run on the recently changed component only. In fact, you're using JavaScript submit() function to submit the entire form. The valueChangeListener will always be executed whenever the new selected value differs from the old value as is been declared in the value attribute.
You don't have declared a value attribute, so its default value is effectively null. If the default selected item of the list is not null, then the valueChangeListener will be invoked.
To fix this, you need to assign a value attribute to the component
<h:selectOneMenu value="#{careNeed.needsLevel}">
and you need to prefill it with the same value as the default value of the dropdown list.
this.needsLevel = "not_assessed";
Alternatively, you can also make the default value null.
<f:selectItem itemValue="${null}" itemLabel="----Not assessed----"/>
Unrelated to the problem, since you're already on JSF 2.0, I'd suggest to use <f:ajax> to submit only the recently changed dropdown by ajaxical powers instead of using onchange="submit()" to submit the entire form. That's after all better for user experience.
<h:selectOneMenu>
<f:ajax />
</h:selectOneMenu>
Also, the valueChangeListener method doesn't need to return anything. It will be ignored anyway. Just declare it void.
You can use AjaxSingle="true" and onsubmit="form.refresh();" on your ajax request.
So that it will process only the current component.
form.refresh(); will remove the old cache value.
You will get the refreshed bean value.

JSF: How to get the selected item from selectOneMenu if its rendering is dynamic?

At my view I have two menus that I want to make dependent, namely, if first menu holds values "render second menu" and "don't render second menu", I want second menu to be rendered only if user selects "render second menu" option in the first menu. After second menu renders at the same page as the first one, user has to select current item from it, fill another fields and submit the form to store values in database. Both the lists of options are static, they are obtained from the database once and stay the same. My problem is I always get null as a value of the selected item from second menu. How to get a proper value? The sample code of view that holds problematic elements is:
<h:selectOneMenu id="renderSecond" value="#{Bean.renderSecondValue}"
valueChangeListener="#{Bean.updateDependentMenus}"
immediate="true"
onchange="this.form.submit();" >
<f:selectItems value="#{Bean.typesOfRendering}" />
</h:selectOneMenu><br />
<h:selectOneMenu id="iWillReturnYouZeroAnyway" value="#{Bean.currentItem}"
rendered="#{Bean.rendered}" >
<f:selectItems value="#{Bean.items}" />
</h:selectOneMenu><br />
<h:commandButton action="#{Bean.store}" value="#Store" />
However, if I remove "rendered" attribute from the second menu, everything works properly, except for displaying the menu for all the time that I try to prevent, so I suggest the problem is in behavior of dynamic rendering. The initial value of isRendered is false because the default item in first menu is "don't render second menu". When I change value in first menu and update isRendered with valueChangeListener, the second menu displays but it doesn't initialize currentItem while submitting.
Some code from my backing bean is below:
public void updateDependentMenus(ValueChangeEvent value) {
String newValue = (String) value.getNewValue();
if ("render second menu" == newValue){
isRendered = true;
} else {
isRendered = false;
}
}
public String store(){
System.out.println(currentItem);
return "stored";
}
If you're using the rendered attribute on UIInput and UICommand components, then you have to make sure that the rendered condition evaluates exactly the same way in the subsequent request (during submitting the form) as it was during the initial request (during displaying the form). When it evaluates false, then the request values won't be applied/validated and the model values won't be updated and no action will be invoked for the component(s) in question.
An easy fix is to put the bean in the session scope, but this has caveats as well (poor user experience when opening same page in multiple browser tabs/windows). On JSF 2.0 you could use the view scope instead. On JSF 1.x you would use Tomahawk's t:saveState for this.
The problem arises only when we have "don't render" as a first option. I decided to change the values in the database and now I have "render second menu" as a first one. With such an approach, everything works fine.

Resources