Is there a way to render a component based on the current value the user has selected from a selectOneMenu component? My selectOneMenu component is populated with an enum consisting of two values, smoker and non-smoker. If the user has smoker selected I want to render a checkbox below it which lets the user check how many they smoke a day 10, 20, 30+, etc. I also want the opposite to work if they user has selected non-smoker i.e. the check boxes don't render/disappear.
Just check the dropdown menu's value in the rendered attribute of the target components and update their common parent by a <f:ajax>. Here's a kickoff example:
<h:selectOneMenu value="#{bean.item}">
<f:selectItem itemValue="one" />
<f:selectItem itemValue="two" />
<f:selectItem itemValue="three" />
<f:ajax render="results" />
</h:selectOneMenu>
<h:panelGroup id="results">
<h:panelGroup rendered="#{bean.item eq 'one'}">
You have selected "one".
</h:panelGroup>
<h:panelGroup rendered="#{bean.item eq 'two'}">
You have selected "two".
</h:panelGroup>
<h:panelGroup rendered="#{bean.item eq 'three'}">
You have selected "three".
</h:panelGroup>
</h:panelGroup>
If you'd like to perform some business logic based on the selected value, use <f:ajax listener>.
<f:ajax listener="#{bean.changeItem}" render="results" />
public void changeItem() {
someResult = someService.getByItem(item);
}
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
How to load and display dependent h:selectOneMenu on change of a h:selectOneMenu
Conditionally displaying JSF components
Related
I have a selectOneMenu with two items and a panelGroup.
When item 1 is selected I want to show the panelGroup and when item 2 is selected hide it.
I try to do it with the onchange event but I don't know how show/hide the panelGroup. Maybe using the panelGroup ID ?
<p:selectOneMenu id="list" value="#{myBean.list}" onchange="???" >
<f:selectItem itemLabel="Item 1" itemValue="Item 1" />
<f:selectItem itemLabel="Item 2" itemValue="Item 2" />
</p:selectOneMenu>
<h:panelGroup id="myPanelGroup">
...
</h:panelGroup>
the change="" will either give u access to a EL Listener or a javascript, which has no update component to it. You're best off adding a ajax call inside the selectOneMenu.
e.g.
<p:selectOneMenu id="list" value="#{myBean.list}">
<f:selectItem itemLabel="Item 1" itemValue="Item 1" />
<f:selectItem itemLabel="Item 2" itemValue="Item 2" />
<p:ajax event="change" process="#this" update="myPanelGroup" />
</p:selectOneMenu>
<h:panelGroup id="myPanelGroup" rendered="#{myBean.list == '1'">
...
</h:panelGroup>
(you need to have rendered on it to ensure it's only shown if your value is 1 (not 2 etc). Although this is the AJAX solution, you could do it using jQuery by binding a change listener to the selectOneMnu 'list' - on change, run your javascript and show/hide the panelgroup div (means u dont need the rendered etc)..
Example:
$("#list").change(function(event){
//get value here and show/hide div using javascript/css what ever you prefer
});
you can use <p:remoteCommand> tag to update you form. And use rendered to show/hide your panel.
see below code and it is working on my side and much easier.
<p:remoteCommand id="remotecommand" name="updatePanel"
update="#form"></p:remoteCommand>
<p:selectOneMenu id="list" value="#{myBean.list}" onchange="updatePanel()" >
<f:selectItem itemLabel="Item 1" itemValue="Item 1" />
<f:selectItem itemLabel="Item 2" itemValue="Item 2" />
</p:selectOneMenu>
<h:panelGroup id="myPanelGroup" rendered="#{myBean.list eq 'Item 1'}">
...
</h:panelGroup>
When the value of list is Item 1 on that time show the panel otherwise it is hide
Rendering content on demand is a common practice in jsf. You should also rethink the use of a two choices selectOneMenu. You have to wrap the content in a panelGroup element and set the inner element to be rendered like this:
<p:selectBooleanCheckbox id="cboOverview" value="#{ctrlBean.bValue}">
<p:ajax event="change" update="outputOverviewWrapper" />
</p:inputSwitch>
<h:panelGroup id="outputOverviewWrapper">
<h:panelGroup id="toggleOverview" rendered="#{!ctrlBean.bValue}">
...
</h:panelGroup>
</h:panelGroup>
If you want to stick with the selectOneMenu you should also use a valueChangeListener to set the rendering attribute properly.
Reference is here.
I have a grid group that should be rendered when change the value of the selectOneMenu. I have attached a valueChangeListener to the selectOneMenu and it access succefully the function assigned to it , but the html component does not rerender. the strange thing that the flag the determines the rendering is called anyway.
<h:selectOneMenu value="#{channelsAdmin.profileID}" id="profileDrp" valueChangeListener="#{channelsAdmin.setProfileInit}">
<f:selectItems value="#{channelsAdmin.profiles}" var="Profile"
itemLabel="#{Profile.profileName}"
itemValue="#{Profile.profileId}" />
<f:ajax execute="#this" immediate="true" render="pnl"></f:ajax>
</h:selectOneMenu>
<h:panelGroup id="pnl" rendered="#{channelsAdmin.ccpAuthPanelFlage}">
<h:inputText id="sest" value="hew">
</h:inputText>
</h:panelGroup>
Bean:
public void setProfileInit(ValueChangeEvent e) {
ccpAuthPanelFlage=true;
ccpPurchPanelFlage=true;
}
The <f:ajax render="pnl"> works roughly as follows:
When the ajax response is arrived, obtain the update element with id="pnl" from XML response.
Now obtain the element with id="pnl" from HTML document by document.getElementById().
Replace its contents with the update element from XML response.
In your particular case, step 2 failed because there's no such element in the HTML document. It doesn't exist in the HTML document because it's never been rendered in first place.
You need to make sure that <f:ajax render="..."> refers an element which is always rendered so that JavaScript can find it by document.getElementById() in order to replace its contents.
<h:selectOneMenu value="#{channelsAdmin.profileID}" id="profileDrp" valueChangeListener="#{channelsAdmin.setProfileInit}">
<f:selectItems value="#{channelsAdmin.profiles}" var="Profile"
itemLabel="#{Profile.profileName}"
itemValue="#{Profile.profileId}" />
<f:ajax execute="#this" immediate="true" render="pnl"></f:ajax>
</h:selectOneMenu>
<h:panelGroup id="pnl">
<h:panelGroup rendered="#{channelsAdmin.ccpAuthPanelFlage}">
<h:inputText id="sest" value="hew">
</h:inputText>
</h:panelGroup>
</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?
I have a small (but vital) problem with JSF and ajax. The form is here:
<h:form>
<h:panelGrid id="pg1" columns="2">
<h:outputText value="Type: "/>
<h:selectOneMenu id="selectOne" value="#{personBean.type}">
<f:selectItems value="#{listBean.personTypes}"/>
<f:ajax event="valueChange" render="pg2"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGrid id="pg2" columns="2">
<h:outputText value="Really bad?" rendered="#{personBean.type=='BAD'}"/>
<h:selectBooleanCheckbox id="checkbox" value="#{personBean.reallyBad}" rendered="#{personBean.type=='BAD'}">
<f:ajax event="click"/>
</h:selectBooleanCheckbox>
</h:panelGrid>
<h:commandButton value="Ajax Submit" action="#{personBean.printValues}">
<f:ajax execute="#form"/>
</h:commandButton>
</h:form>
The PersonBean is a simple bean with an enum PersonType that has two values: NICE, BAD and a Boolean called reallyBad. The ListBean returns the enum values in a list to populate the selectOneMenu.
Basically when I select BAD the panel for the boolean checkbox is rendered where I can tick it to say a person is reallyBad. I can then submit the form if I wish. The problem is when I tick the checkbox and then select NICE again, the checkbox is still ticked even though it is not rendered. So when I submit my form the person can be NICE and reallyBad, which doesn't make sense.
Rather than having to select BAD again to uncheck the box, is their a way that I can reset the checkbox and its input value to false when NICE is selected? I'm a bit of a noob to ajax with JSF! Thanks.
ps. I am printing the values of the two inputs on submit with the commandButtons action to verify the results...
You need to manually clear the checkbox when you change the menu.
<f:ajax listener="#{personBean.setReallyBad(false)}" render="pg2" />
By the way, the both <f:ajax event> values as you've in your code are the default values already. Just omit them.
I have a table that uses a radio button to select a row. When the radiobutton is clicked ajax executes some code to update another field and then render the form. Everything works fine, except that the radio selection disappears and all the radio buttons are unselected. I'm not sure why this is happening? The button was clicked so the value shouldn't change. And it's not the onchange javascript, I removed that as a test and still the same problem. I also can't just render the updated field because it's separate from the code below, I get a servlet exception - unknown id error (maybe someone could tell me how to get around that also?)
<h:dataTable id="addClient" styleClass="dataTable"
value="#{AddEntryMB.clientValues}" var="c" binding="#{AddEntryMB.dataTable}"
rendered="#{AddEntryMB.renderClientTable}">
<f:facet name="header" >
Select Client to Associate with Appointment
</f:facet>
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio valueChangeListener="#{AddEntryMB.setSelectedItem}"
immediate="true" onchange="dataTableSelectOneRadio(this);" >
<f:selectItem itemValue="null" />
<f:ajax event="click" render="#form"/>
</h:selectOneRadio>
</h:column>
Look like the value of your selection isn't saved in the backing bean.
First you should bind your h:selectOneRadio component to a backing bean value:
<h:selectOneRadio valueChangeListener="#{AddEntryMB.setSelectedItem}"
value=#{c.radioValue}
immediate="true" onchange="dataTableSelectOneRadio(this);" >
then try to process the dataTable within your ajax like:
<f:ajax event="click" render="#form" execute="addClient"/>
Also I would also add an itemLabel beside itemValue to see what is selected:
<f:selectItem itemValue="null" itemLabel="NULL"/>
It gets unselected because the value is not been set in a model value and the form get refreshed by ajax. You could prevent the unselection by binding the value to the model.
<h:selectOneRadio ... value="#{AddEntryMB.selected[c.id]}">
with
private Map<Long, Boolean> selected = new HashMap<Long, Boolean>(); // +getter
In this example, I assume that the type represented by #{c} has a private Long id property which represents the unique ID.
I have a panel grid with 2 columns, the first containing a label and the second a component.
The listener backingBean.onFunktionChange sets the value of backingBean.functionGraduand to true. So the label and the menue for the degree are rendered in this case.
The problem is that if I first select a city in the menu for city and then change the value in the menu "function", the whole panel is updated and thus the city menu is reset.
However, in case of function change I want to to keep the preselected city.
If I wrap the label and component for function and degree in a panelGroup with id=panelFunction lets say and only update this panel, destroys the two column layout.
Is there any possibility to only update a part of the panelGrid without affecting the layout of the panelGrid?
<h:panelGrid columns="2" id="panel">
<label>#{function}</label>
<p:selectOneMenu id="menuFunction" value="#{...}" >
<p:ajax event="change" process="menuFunction"
update="panel"
listener="#{backingBean.onFunctionChange}" />
<f:selectItems .../>
</p:selectOneMenu>
<h:outputLabel id="labelDegree" for="menuDegree"
value="#{msgs.degree}"
rendered="#{backingBean.functionGraduand}" />
<p:selectOneMenu id="menuDegree" value="#{...}" rendered="#{backingBean.functionGraduand}">
<f:selectItems .../>
</p:selectOneMenu>
<label class="required">#{msgs.city}</label>
<p:selectOneMenu value="#{...}" id ="city">
<f:selectItems .../>
</p:selectOneMenu>
</h:panelGrid>
I solved this by processing the whole panel in the ajax request:
...
<p:ajax event="change" process="panel"
update="panel"
listener="#{backingBean.onFunctionChange}" />
...