How to get selected items from p:selectManyCheckbox? - jsf

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!

Related

JSF - How can I retain the transient values within a ui:repeat after an immediate action

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.

Dynamic update primefaces component according to a autocomplete value

Someone can explain how to update an certain component in a view (example: dataScroller, dataList etc) using an autocomplete component ? I'm trying something with the ajax event "itemSelect" but with no success on how to proceed, any example, explanation, light will be a good help.
Some code (xhtml page)
<h:form id="frmPesquisarModalidade">
<p:autoComplete id="autoModalidade" multiple="true" value="#{matriculaBean.modalidadesSelecionadas}" completeMethod="#{matriculaBean.completeModalidade}"
var="modalidade" itemLabel="#{modalidade.nome}" itemValue="#{modalidade}" forceSelection="true">
<f:converter binding="#{modalidadeConverter}"/>
<f:ajax listener="#{matriculaBean.onItemSelect}" event="itemSelect" render="test"/>
<p:column style="width:10%">
<h:outputText value="#{modalidade.nome}"/>
</p:column>
</p:autoComplete>
<p:dataScroller id="dataScrollModalidadesSelecionadas" value="#{matriculaBean.modalidadesSelecionadas}" var="modalidade" chunkSize="6">
<h:panelGrid columns="2" cellpadding="5" id="test">
<h:outputText value="#{modalidade.nome}"/>
</h:panelGrid>
</p:dataScroller>
</h:form>
backing bean
#Named
#ViewScoped
public class MatriculaBean {
private List<Modalidade> modalidadesDisponiveis;
private List<Modalidade> modalidadesSelecionadas;
#Autowired
private ServicoModalidade servicoModalidade;
#PostConstruct
private void init(){
modalidadesDisponiveis = servicoModalidade.listar();
}
public List<Modalidade> completeModalidade(String busca) {
List<Modalidade> filtrados = new ArrayList<Modalidade>();
for (Modalidade mod : modalidadesDisponiveis) {
if (mod.getNome().contains(busca)) {filtrados.add(mod);}
}
return filtrados;
}
public void onItemSelect(SelectEvent event){
//????????
}
What you need to do in onItemSelect method is update the value of the target component, based on the selected item.
If you want to update dataScrollModalidadesSelecionadas:
public void onItemSelect(SelectEvent event){
//Get the selected item value
Modalidade m = new Modalidade();
m = event.getObject();
//Create/use a List method which can search the DB based on the selected item
modalidadesDisponiveis = servicoModalidade.listarPorModalidade(m);
}
And in the JSF, you just have to update the target component:
<p:autoComplete ...... >
<p:ajax listener="#{matriculaBean.onItemSelect}" event="itemSelect" update="dataScrollModalidadesSelecionadas"/>
</p:autoComplete>
I'm not sure if those options and properties you defined gonna work as expected, but the example above is the basics to update a component based on ItemSelect.
Also, I prefer using <p:ajax> rather than <f:ajax>
<h:form id="frmPesquisarModalidade">
<p:autoComplete id="autoModalidade" multiple="true" value="#{matriculaBean.modalidadesSelecionadas}" completeMethod="#{matriculaBean.completeModalidade}"
var="modalidade" itemLabel="#{modalidade.nome}" itemValue="#{modalidade}" forceSelection="true">
<f:converter binding="#{modalidadeConverter}"/>
<p:ajax event="itemSelect" update="dataScrollModalidadesSelecionadas"/>
<p:column style="width:10%">
<h:outputText value="#{modalidade.nome}"/>
</p:column>
</p:autoComplete>
<p:dataScroller id="dataScrollModalidadesSelecionadas" value="#{matriculaBean.modalidadesSelecionadas}" var="modalidade" chunkSize="6">
<h:panelGrid columns="2" cellpadding="5">
<h:outputText value="#{modalidade.nome}"/>
</h:panelGrid>
</p:dataScroller>
</h:form>
solved!!! to resolve my issue i'd discovered theres no need to apply an backing bean logic withe the itemSelect event... just create an ajax event in the xhtml page and use the update attribute with the respective id! Thanks for help

JSF update inputText after selectOneMenu choice

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

conditional rendered f:selectItem possible problems

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" />

Change the properties of an Input within a ui:repeat

I'd like to change the "required" property of an InputText that is located within an ui:repeat, but I'm not able to access to the component from the ManagedBean:
<h:selectManyCheckbox id="required" value="#{test.required}"
layout="lineDirection" converter="javax.faces.Integer">
<f:ajax event="change" listener="#{test.update}" />
<f:selectItems value="#{test.selectable}"></f:selectItems>
</h:selectManyCheckbox>
<ui:repeat value="#{test.names}" var="name" id="repeat">
<h:panelGrid columns="3">
<h:outputLabel id="nameLabel">name:</h:outputLabel>
<h:inputText id="name" value="#{name}"
validator="#{test.validateName}" />
<h:message for="name"></h:message>
</h:panelGrid>
</ui:repeat>
I'm trying to use the findComponent method, but it does not work:
public void update(AjaxBehaviorEvent event) {
for(Integer i: selectable) {
UIViewRoot vr = FacesContext.getCurrentInstance().getViewRoot();
HtmlInputText input = (HtmlInputText)vr.findComponent("form:repeat:"+i+":name");
input.setRequired(required.contains(i));
}
}
The ui:repeat doesn't repeat the components in the view root, it repeats the component's output in the rendered HTML output.
There are several ways to achieve this properly. One of them is to use a value object instead and set the requireness there. E.g. a List<Item> wherein Item has the properties String name and boolean required.
<ui:repeat value="#{test.items}" var="item" id="repeat">
<h:panelGrid columns="3">
<h:outputLabel id="nameLabel">name:</h:outputLabel>
<h:inputText id="name" value="#{item.name}" required="#{item.required}" validator="#{test.validateName}" />
<h:message for="name"></h:message>
</h:panelGrid>
</ui:repeat>
There are more ways, but since the JSF version you're using and the functional requirement is unclear, it's only guessing which way is the most applicable in your case.

Resources