I have a page with several h:selectOneMenu or p:selectOneMenu and I want to use the same page for editing and adding data.
When I will edit data I need f:selectItem. I know that this component doesn't have attribute rendered. And I read that I can use <c:if>.
Ok. For example, if I write
<p:selectOneMenu rendered="#{not empty bean.id}"
value="#{bean.selectedId}">
<c:if test="${editableBean != null}">
<f:selectItem itemLable="#{editableBean.name} itemValue=#{editableBean.id} />
</c:if>
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
Will it works without any problems in primefaces and with ajax listeners?
The easy solution (but with poor performance) will be to have a boolean editMode attribute in your managed bean to enable/disable the components. Basic example:
<p:selectOneMenu rendered="#{not empty bean.id}" disabled="#{bean.editMode}"
value="#{bean.selectedId}">
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
In your bean
#ManagedBean
#ViewScoped
public class Bean {
private int id;
private boolean editMode;
//other attributes...
//getters and setters...
#PostConstruct
public void init() {
//a way to know if the bean it's in edit mode
editMode = (id != 0);
}
}
This solution will have poor performance because every <p:selectOneMenu> will have to load all the data and then select the actual value, but it will do what you want. Another option will be to use this attribute for the rendered property of <p:selectOneMenu> and for an <h:inputText disabled="true" readonly="true" /> (or maybe <h:outputText />). Another basic sample:
<p:selectOneMenu rendered="#{not empty bean.id && not bean.editMode}"
value="#{bean.selectedId}">
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
<h:inputText rendered="#{bean.editMode}" value="{bean.selectedText}"
disabled="true" readonly="true" />
Related
I'm using JSF 2.2.8 and primefaces 6.0, and i have a selectCheckBoxMenu i want to retrieve the selected values in my bean.
The selectCheckboxMenu is filled from the database but when i select the attributes and I save nothing happens it does not call the save function
Here is my selectCheckBoxMenu
<p:outputLabel for="ressource" value="Ressource"/>
<h:panelGroup >
<p:selectCheckboxMenu id="ressource" label="Ressource" value="#{affectationBean.selectedRessource}" multiple="true">
<f:selectItems value="#{affectationBean.ressources}" var="r" itemLabel="#{r.nom}" itemValue="r.idt_ressource" />
</p:selectCheckboxMenu>
</h:panelGroup>
<p:commandButton icon="ui-icon-save" actionListener="#{affectationBean.save}" value="Save" update="#affectation" ajax="false" style="display:inline-block;margin-top:5px"/>
Here is the the declaration of the selectedRessource and the actionListener save
private Long [] selectedRessource;
// Getters setters and Construct
public void save(){
for(int i=0 ;i<selectedRessource.length;i++){
system.out.println("id ===> " + selectedRessource[i]);
}
My suggestion would be:
First make sure everything is inside the h:form tag.
don't need to multiple = true as this tag does not take this attribute
i tested with below modification and got the selected multiple value in my bean. The only difference is i am using same value for itemLabel and itemValue but in your case it is object. i am using primefaces 6 also and dont even need to change actionListner to action. It is working as it is.sample xhtml
sample ResourceBean.java
<p:outputLabel for="ressource" value="Ressource"/>
<h:panelGroup >
<p:selectCheckboxMenu id="ressource" label="Ressource" value="#{resourceBean.selectedRessource}">
<f:selectItems value="#{resourceBean.ressources}" var="r" itemLabel="#{r}" itemValue="#{r}" />
</p:selectCheckboxMenu>
</h:panelGroup>
<p:commandButton icon="ui-icon-save" actionListener="#{resourceBean.save}" value="Save" ajax="false" style="display:inline-block;margin-top:5px"/>
The problem is in your p:commandButton, you have 3 options
change your method:
public void save(ActionEvent actionEvent){...}
change your action listener value:
actionListener="#{affectationBean.save()}"
or change your button to use action
action="#{affectationBean.save}"
DISCLAIMER: This is a workaround. It is not intended to be a permanent solution but will allow you to use selectCheckboxMenu and keep working.
There is an issue with this component that prevents it from passing values to the backing bean upon submit.
For some reason the array that should contain the selected values gets cleared out upon submit. Therefore I made an extra array that I did not declare in the tag, and updated in on every change event. Then on submit the values were still there. See below:
BackingBean.java
private String[] sCodes;
private String[] sCodes2; //extra array, not in form.xhtml
public void updateCodes()
{
sCodes2 = sCodes; //keeps the values in the other array
}
public void doSend() throws IOException
{
log.trace("selected codes: {} selected codes2 length: {}", sCodes.length, sCodes2.length);
}
form.xhtml
<p:selectCheckboxMenu id="codeCtl" value="#{bean.SCodes}" label="Codes" filter="true" filterMatchMode="startsWith" panelStyle="width:250px">
<f:selectItems value="#{bean.menuCodes}" />
<p:ajax event="change" listener="#{bean.updateCodes()}" />
</p:selectCheckboxMenu>
<p:commandButton value="submit" actionListener="#{bean.doSend}" id="ctlSubmit" update="appform"/>
I have a selectManyCheckbox which have a list of items shown, When ever I select an item I want a callback on my backing bean to be triggered and then get the value of the selected item for doing some filtering with this value. My problem is that I can't get the backing bean method to be executed. Have tried several ways, here's my code
1
<h:form class="block filter image-list-filter">
<div class="title-block"> FILTER </div>
<div class="content">
<ul class="filter-block">
<p:selectManyCheckbox id="vals" layout="grid"
valueChangeListener="#{bean.selectFilter}" onchange="submit();">
<p:ajax event="click" process="#form" update="#all"/>
<f:selectItems value="#{bean.options}" var="filter"
itemValue="#{filter.idFilter}" itemLabel="#{filter.descr}"
itemDescription="#{filter.image}"/>
</p:selectManyCheckbox>
</ul>
</div>
</h:form>
2
<p:selectManyCheckbox id="vals" layout="grid">
<p:ajax event="click" process="#form" update="#all"
listener="#{bean.selectFilter}" />
<f:selectItems value="#{bean.options}" var="filter"
itemValue="#{filter.idFilter}" itemLabel="#{filter.descr}"
itemDescription="#{filter.image}"/>
</p:selectManyCheckbox>
3
<p:selectManyCheckbox id="vals" layout="grid"
valueChangeListener="#{bean.selectFilter}">
<p:ajax event="click" process="#form" update="#all"/>
<f:selectItems value="#{bean.options}" var="filter"
itemValue="#{filter.idFilter}" itemLabel="#{filter.descr}"
itemDescription="#{filter.image}"/>
</p:selectManyCheckbox>
Backing Bean Method
public void selectFilter(ValueChangeEvent dege) {
Object[] selFilters = (Object[]) dege.getNewValue();
if (selFilters.length != 0) {
//Do stuff
}
}
The options attribute is an ArrayList of a particular object that stores the available values. Of course, this attribute has setter & getter. PF version is 5.0
There are a lot of things in your code that is done much differently than I would have done it, and this makes it difficult for me to determine exactly what is causing the problem. Instead, I will explain how I would have solved it:
Component:
<p:selectManyCheckbox id="vals" layout="grid" value="#{bean.selectedValue}">
<p:ajax listener="#{bean.selectFilter}" update="#all"/>
<f:selectItems value="#{bean.options}" var="filter"
itemValue="#{filter.idFilter}" itemLabel="#{filter.descr}"
itemDescription="#{filter.image}"/>
</p:selectManyCheckbox>
Bean:
private Object[] selectedValue;
public Object[] getSelectedValue(){
return selectedValue;
}
public void setSelectedValue(Object[] newValue){
selectedValue = newValue;
}
public void selectFilter(){
Object[] selFilters = getSelectedValue();
if (selFilters.length != 0) {
//Do stuff
}
}
As far as I know this is the standard way of doing things, submitting the values through the normal property setter and then interacting with it in a triggered method like selectFilter. I would also discourage the usage of update="#all" as this will refresh the entire page. Updating only the parts of the page that actually changes with something like update="#(.image-list)" will tend to give a better user experience.
For getting selected items use h:form tags!
I use 3 selectOneMenu components in my project. I need to reload content of the second after change in the first. Here are some parts of the files
index.xhtml
<h:form id="form">
<p:selectOneMenu id="Rząd" value="#{birdSelectorBean.selectedState}" effect="fade"
style="width: 150px;">
<f:selectItem itemLabel="Rząd" itemValue="" />
<f:selectItems value="#{birdSelectorBean.rzad}" />
<p:ajax render="#form" listener="#{birdSelectorBean.stateChangeListener}" />
</p:selectOneMenu>
<p:selectOneMenu id="rodzina" value="#{birdSelectorBean.selectedState}" effect="fade"
style="width: 150px;">
<f:selectItem itemLabel="Rodzina" itemValue="" />
<f:selectItems value="#{birdSelectorBean.rodzina}" />
</p:selectOneMenu>
<p:selectOneMenu id="rodzaj" value="#{birdSelectorBean.selectedState}" effect="fade"
style="width: 150px;">
<f:selectItem itemLabel="Rodzaj" itemValue="" />
<f:selectItems value="#{birdSelectorBean.rodzaj}" />
</p:selectOneMenu>
</h:form>
BirdSelectionBean.java:
public class BirdSelectorBean
{
private String selectedState;
private List<SelectItem> rzad;
private List<SelectItem> rodzina;
private List<SelectItem> rodzaj;
public BirdSelectorBean()
{
rzad = new ArrayList<>();
rzad.add(new SelectItem("Rząd_X"));
rzad.add(new SelectItem("Rząd_Y"));
rzad.add(new SelectItem("Rząd_Z"));
rodzina = new ArrayList<>();
rodzaj = new ArrayList<>();
}
public void stateChangeListener(ValueChangeEvent event)
{
rodzina.clear();
rodzina.add(new SelectItem("Rodzina_A"));
rodzina.add(new SelectItem("Rodzina_B"));
rodzina.add(new SelectItem("Rodzina_C"));
}
...
getters and setters
...
}
I read many topics on that but it doesn't work for me. I tried update="rodzina" like it is in example
here
and render option like it is said in that
topic:
But it still doesn't work. Please help me :]
In the p:ajax tag change render="#form" to update="#form". Render is used by f:ajax, primefaces use another name for some reason - see here.
Looks like your stateChangeListener method nevers get called and more important, your managed bean looks like hasn't any scope (at least from your question content), remember that it must be at least #ViewScoped in order to make this work. Also, another problem in your code is that you're using the same attribute to select the data for the three <p:selectOnuMenu> (which is not a problem yet since you haven't achieved what you wanted to begin with).
To make the ajax update work, remove the parameter from your stateChangeListener. Also let's add the other two attributes for the selected items from the dropdownlists.
#ManagedBean
#ViewScoped
public class BirdSelectorBean {
private String selectedState;
private String selectedStateRodzina;
private String selectedStateRodzaj;
//other fields and methods...
public void stateChangeListener() {
rodzina.clear();
rodzina.add(new SelectItem("Rodzina_A"));
rodzina.add(new SelectItem("Rodzina_B"));
rodzina.add(new SelectItem("Rodzina_C"));
}
}
And then update your desired <p:selectOneMenu> in your <p:ajax> call (I removed the non-directly related to the problem attributes from the components like style):
<p:selectOneMenu id="Rząd" value="#{birdSelectorBean.selectedState}">
<f:selectItem itemLabel="Rząd" itemValue="" />
<f:selectItems value="#{birdSelectorBean.rzad}" />
<p:ajax update="rodzina" listener="#{birdSelectorBean.stateChangeListener}" />
</p:selectOneMenu>
<p:selectOneMenu id="rodzina" value="#{birdSelectorBean.selectedStateRodzina}">
<f:selectItem itemLabel="Rodzina" itemValue="" />
<f:selectItems value="#{birdSelectorBean.rodzina}" />
</p:selectOneMenu>
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
The problem occurs with this code:
<h:form>
<rich:panel>
<f:facet name="header">
<h:selectManyCheckbox title="Select which types of requests you want to see"
onchange="submit();" value="#{filterListener.chosenFilters}"
id="selectBoxContainer" >
<f:selectItem id="approvedByITS" itemLabel="Approved by ITS" itemValue="approvedByITS" />
<f:selectItem id="approvedByPO" itemLabel="Approved by Process Owner" itemValue="approvedByPO" />
<f:selectItem id="dob" itemLabel="Date" itemValue="dob" />
<f:selectItem id="externalAssignedTo" itemLabel="External assigned" itemValue="externalAssignedTo" />
<f:selectItem id="internalAssignedTo" itemLabel="Internal assigned" itemValue="internalAssignedTo" />
<f:selectItem id="ITSapprovedBy" itemLabel="ITS approved by" itemValue="ITSapprovedBy" />
<f:selectItem id="severity" itemLabel="Severity" itemValue="severity" />
<f:selectItem id="status" itemLabel="status" itemValue="status" />
<f:valueChangeListener type="no.ngt.tech.rt2.listeners.requestFilterListener" />
</h:selectManyCheckbox>
</f:facet>
<h:dataTable value="#{filterListener.chosenFilters}" var="selects" >
<h:column>
<h:outputText value="#{selects}" />
</h:column>
</h:dataTable>
<br />
<h:messages />
</rich:panel>
</h:form>
As we can see I have the value="#{filterListener.chosenFilters}". The dataTable's value is also the same, so whenever I click one of the selectItem's the dataTable has an element added or removed from it (this is working). In my backing bean I have the following code:
public class requestFilterListener implements ValueChangeListener {
private List<String> chosenFilters;
public requestFilterListener() {
}
public void processValueChange(ValueChangeEvent event) {
System.out.println("processValueChange called");
if (chosenFilters != null) {
System.out.println(chosenFilters.size());
}
}
public List<String> getChosenFilters() {
return chosenFilters;
}
public void setChosenFilters(List<String> chosenFilters) {
this.chosenFilters = chosenFilters;
}
Everytime I click one of the checkboxes, a column is added/removed with the proper data, in my console I get the message "processValueChange called" as I output in the processValueChange method, but during this time chosenFilters is always null, and the if expression is never run. How come? This is a session bean, and I really dont understand why my list cant be used within the backing bean, but is used without a problem by my dataTable.
Thanks for your time in looking into this.
The problem is probably on this tag:
<f:valueChangeListener type="no.ngt.tech.rt2.listeners.requestFilterListener" />
You are instructing the f:valueChangeListener tag to create a new instance of requestFilterListener instead of binding to the one specified by the managed bean configuration. Use the binding attribute to bind to #{filterListener}.