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"/>
Related
In normal circumstances like this:
<h:form>
<h:selectOneRadio value="#{bean.gender}">
<f:selectItem itemValue="Male" itemLabel="Male" />
<f:selectItem itemValue="Female" itemLabel="Female" />
<f:selectItem itemValue="Other" itemLabel="Other" />
</h:selectOneRadio>
<h:commandButton value="Submit" action="#{bean.action}" />
</h:form>
Selecting one radio button disselects the other & the radio button will be remain selected on the postback. (when the same view is rendered)
However, when we're dealing with an iterating component like <h:dataTable>, the selection is lost.
Consider the snippet:
<h:form id="hashMapFormId">
<b>HASH MAP:</b>
<h:dataTable value="#{playersBean.dataHashMap.entrySet()}" var="t" border="1">
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio id="radiosId" onclick="deselectRadios(this.id);"
value="#{playersBean.select}">
<f:selectItem itemValue="null"/>
</h:selectOneRadio>
</h:column>
</h:dataTable>
<h:commandButton value="Show Hash Map Selection"
action="#{playersBean.showSelectedPlayer()}" />
</h:form>
With disselecting the other radio buttons when one radio button is selected being implemented by simple JavaScript-
function deselectRadios(id) {
var f = document.getElementById("hashMapFormId");
for (var i = 0; i < f.length; i++)
{
var e = f.elements[i];
var eid = e.id;
if (eid.indexOf("radiosId") !== -1) {
if (eid.indexOf(id) === -1) {
e.checked = false;
} else {
e.checked = true;
}
}
}
}
Fire the GET request:
Select a radio button:
Now press the submit button, response:
You see that the radio button gets dis selected on postback. How to solve this shortcoming?
I know it very well that this is due to this component attribute itemValue being null:
<f:selectItem itemValue="null"/>
This trick is a leftover from JSF 1.x / 2.0/2.1 when it wasn't possible to use a <h:selectOneRadio> for single row selection in a <h:dataTable>. This trick originated in my 10 year old blog article Using Datatables - Select row by radio button.
The root problem is, HTML radio buttons are grouped based on their name attribute, so the webbrowser knows which others to unselect when one is selected. But JSF generates by design a different one for each <h:dataTable> item, with the row index inlined and therefore they can't be grouped and hence the JavaScript based workaround.
Since JSF 2.2, with the new passthrough elements and attributes feature, it's however possible to force the name attribute to the value of your choice and capture the selected item via a helper <h:inputHidden>. This is fleshed out in another blog article of me, from previous year: Custom layout with h:selectOneRadio in JSF 2.2. The article uses <ui:repeat> as an example, this can be rewritten to <h:dataTable> as below.
<h:form>
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
value="#{item.id}" a:checked="#{item.id eq bean.selectedItemId ? 'checked' : null}" />
</h:column>
<h:column>#{item.id}</h:column>
<h:column>#{item.name}</h:column>
</h:dataTable>
<h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItemId}"
rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>
#Named
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private Long selectedItemId;
// ...
public void submit() {
System.out.println("Selected item ID: " + selectedItemId);
}
// ...
}
And yes, the selected radio button remains selected on postback this way. You can also pass whole entities, this only requires a converter on the <h:inputHidden>.
I created a very simple example based on my project in order to illustrate my doubt. Just a way to register a person with a list of telephone numbers.
MainController.java
private String name;
private List<Phone> phoneList;
// Getters and Setters
#PostConstruct
private void init() {
phoneList = new ArrayList<>();
}
public static class Phone implements Serializable {
private String number;
// Getters and Setters
#Override
public String toString() {
return number != null ? number : "null";
}
}
public void add() {
phoneList.add(new Phone());
}
public void save() {
System.out.println("Name: " + name + "; " + phoneList.toString());
}
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
In my example, note that all phone fields that are added MUST be filled in (required = true).
The problem is: when I type name and click add (to add a phone) the value of the field is maintained. But when I type a first phone and click add, the phone's value is not maintained. This occurs for all fields within the component ui:repeat.
Is there a way to preserve the input values within a after an immediate request, as with the name field?
Extra note: Other strange behavior I noticed is when add at least two phone fields, let the first blank and fills the second, and saves the form. After a failed validation (due to phone blank), click add will make all fields are filled with the value of the second phone.
Wildfly 9.0.2, JSF Api (Jboss) 2.2.12
Thanks to #BalusC comment. The OmniFaces library has two taghandlers that can be used in this case. In both cases input values will be preserved in case of validation failure. Note that h:commandButton should be with <h:commandButton immediate="false" />.
ignoreValidationFailed
In this case all validation failures will be ignored (including converter failures). Note that the h:form have to be changed to o:form. Also, the failures messages will still be displayed, which can be solved putting a proper condition in the rendered attribute. The files will look like this:
index.xhtml
<o:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:ignoreValidationFailed />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</o:form>
<h:messages rendered="#{facesContext.validationFailed}" />
skipValidators
In this case only the validation failures will be ignored (the converters will still run). The failures messages will not be displayed, except for the converters. Note that this taghandler is only available since the 2.3 version. The files will look like this:
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:skipValidators />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
The solution that I use to this problem is to create an external field to the loop, which stores a JSON containing the values that should be saved. This field, to be outside the loop, properly saves values after each try and restore the missing values when necessary. I use two functions JavaScript and JQuery library.
So the files would look like this:
index.xhtml
<h:outputScript library="jquery" name="jquery.min.js" />
<h:outputScript library="all" name="all.js" />
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText styleClass="savePhoneNumber" value="#{phone.number}" required="true" onchange="saveUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')" />
</ui:repeat>
<h:inputHidden id="allPhoneNumber" binding="#{allPhoneNumber}" />
<h:outputScript>loadUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')</h:outputScript>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
all.js
function saveUiRepeatInput(inputAll, inputClass) {
document.getElementById(inputAll).value = JSON.stringify($('.' + inputClass).map(function() { return this.value; }).get());
}
function loadUiRepeatInput(inputAll, inputClass) {
var jsonAll = document.getElementById(inputAll).value;
if (jsonAll) {
var array = JSON.parse(jsonAll);
$('.' + inputClass).each(function(i) { if (i < array.length) this.value = array[i]; });
}
}
Although work perfectly (including via ajax, with some minor changes), it looks like a hack, not an ideal solution. So if anyone can help with any solution strictly based on JSF, I will be grateful. Thanks.
I have a form that lets me edit a list of beans (one at a time), using buttons I can switch between the beans.
Keeping it simple :
public class MyBean {
private String text;
}
public class MyController {
private List<MyBean> availableBeans = new ArrayList<MyBean>(); // has five MyBeans with random text
private MyBean selectedBean; // initialized with first element of list
private int index = 0;
public void nextBean() { index++; }
public void previousBean() { index--; }
private void refreshBean() { selectedBean = availableBeans.get(index); }
}
For the html part I have something like
<h:form id="someForm">
<!-- stuff -->
<p:inputText value="#{myController.selectedBean.text}" />
<p:inplace editor="true" label="#{myController.selectedBean.text}" >
<p:inputText value="#{myController.selectedBean.text}" />
</p:inplace>
<!-- more stuff-->
</h:form>
If I change the text inside the inplace tag, the variable in myBean will be updated just fine, but If I only use inputText the bean will still have the old value, even if I change it on the webpage. Why is that?
Its because the p:inplace editor="true" implicitly submits the value to the server while <p:inputText does not do it implicitly,
You can solve it in several ways
1) add submit button like <p:commandButton to submit the value from p:inputText
2) use p:ajax event="keyup" or event="change",inside p:inputText
also take a look at the showcase p:ajax enables ajax features on supported components.
p.s , remove the value attribute from the p:inplace (there is no such attribute in p:inplace)
Lets give your components ids:
<h:form id="someForm">
<p:inputText id="first" value="#{myController.selectedBean.text}" />
<p:inplace id="second" editor="true" value="#{myController.selectedBean.text}">
<p:inputText id="third" value="#{myController.selectedBean.text}" />
</p:inplace>
</h:form>
According to the Primefaces Documentation 3.5 the component p:inplace has no attribute called value.
Do you submit the form someForm when changing the value of first? Otherwise the updated values from first won't be passed to MyController and MyBean. p:inplace submits the values automatically whereby you have to do it yourself it you use the standard p:inputText.
I want to achieve sth similar to http://www.primefaces.org/showcase/ui/pprSelect.jsf but i need a collection of double-combos, so i wrapped it in ui:repeat
I need on the backend check which element from collection of double-combos was changed and what I need to reload. For communication is used p:ajax as in the example, but AjaxBehaviorEvent not bring me any idea of index of element ( i mean index of double-combos element generated by ui:repeat)
My client code, the idea is to update bean:selectedIndex everytime when a ajax event will be raised ( on change value of selectOneMenu ), and value of bean:selectedIndex will be set as index of changed selectOneMenu
private List<State> productStates
private int selectedIndex;
private List<Group> groups;
private Map<Integer, Collection<Device>> availableDevicesMap;
<ui:repeat var="state" value="#{bean.productStates}" varStatus="iter">
<p:selectOneMenu id="devGroup" value="#{state.group}">
<f:selectItems value="#{bean.groups}" />
<p:ajax update="refreshable" process="devGroup, #this" listener="#{bean.refreshDevicesForState}" >
<f:setPropertyActionListener target="#{bean.selectedIndex}" value="#{iter.index}"/>
</p:ajax>
</p:selectOneMenu>
<!-- THIS WILL BE UPDATED -->
<h:panelGroup id="refreshable">
<p:selectManyButton id="devices" value="#{state.devices}" >
<f:selectItems value="#{bean.availableDevicesMap[status.index]}" />
</p:selectManyButton>
</h:panelGroup>
</ui:repeat>
Backend which doesn't work as expected. setPropertyActionListener is not invoked and selectOneMenu component hasn't got selected group as value
public refreshDevicesForState(AjaxBehaviorEvent e) {
SelectOneMenu menu = (SelectOneMenu)e.getComponent();
// this value is not as selected on frontend
Group group = (Group)menu.getValue();
// selectedIndex will not be set, so I assume that setPropertyActionListener didn't invoked
availableDevicesMap.put(selectedIndex, group.getDevices());
}
I tried also with code below which works but in my opinion it is ugly
// id will be grandpaId:parentId:index:myId
String selectedIndex = IdHelper.getIdPart(e.getComponent().getClientId(), -2);
State state = productStates.get(Integer.parseInt(selectedIndex));
I am using latest primefaces on glassfish and Mojarra as jsf reference implementation
Thank you for any help
In more general sense:
I have list of objects on backed bean, lets say Cars
List<Car> cars
on frontent I iterate over them and create select brand and select model combos for every car. When user select brand for i.e 4th car i want to get to know on backend that 4th car will be changed and i will reload list of available model for this one car
<ui:repeat var="state" value="#{bean.cars}" >
<p:selectOneMenu id="brands"/>// select brand
<p:selectOneMenu "models"/>// show available models depends on selected brand
</ui:repeat>
How to handle it correct in the JSF world ?
My first suggestion is to use converter for Group.
SelectOneMenu cannot set custom class, only with the help of a converter. (an example is at autocomplete: http://www.primefaces.org/showcase/ui/autoCompletePojo.jsf)
Second, in your bean handler, productStates variable contains already the selected values (of selectOneMenus). You can use it easier, than access it from the event.
If the values of selectOneMenus depend on State, you have to modify this:
<f:selectItems value="#{bean.groups}" />
to be able to express which group values should be displayed.
If you want debug it (without eclipse debugging), you can use messages, for example:
add this to xhtml:
<p:growl id="msgs" showDetail="true"/>
and in bean:
public refreshDevicesForState(AjaxBehaviorEvent e) {
...
FacesMessage msg = new FacesMessage("Selected", "any debug info" + productStates.get(0).getGroup());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
I modified my answer, according to your mods. I would do it this way:
xhtml:
<ui:repeat var="state" value="#{bean.productStates}" varStatus="iter">
<p:selectOneMenu id="devGroup#{iter.index}" value="#{state.group}"
valueChangeListener="#{bean.updateSubProperty}" immediate="true">
<f:selectItems value="#{bean.groups}" />
<f:attribute name="index" value="#{iter.index}" />
</p:selectOneMenu>
<p:selectOneMenu id="subDevGroup#{iter.index}">
...
</p:selectOneMenu>
</ui:repeat>
bean:
public void updateSubProperty(ValueChangeEvent vce) {
String index = vce.getComponent().getAttributes().get("index").toString();
int i = Integer.parseInt(index); //this is the index of the selected selectOneMenu
///...
//update sub selectOneMenu
RequestContext.getCurrentInstance().update("subDevGroup" + index);
}
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" />