How to make p:selectOneMenu show current value? - jsf-2

I have a <p:selectOneMenu> to choose the category for an item from. By default the menu displays the first entry of someBean.selectedParty.categories as selected. But the item already has a current category at someBean.selectedItem.category that I want to be displayed/selected. How can I achieve that?
<p:selectOneMenu value="#{someBean.selectedItem.category}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{someBean.selectedParty.categories}" var="category" itemLabel="#{category.name}"/>
</p:selectOneMenu>

As long as the getter for someBean.selectedItem.category returns a value available in the list , it will show the value as selected.
However there are some gotchas:
1) The default value probably should be set in the bean constructor
2) The bean (someBean) cannot have the RequestScope - As every single HTTP request, including Ajax requests, would create a completely brand new instance of the bean with all properties set to default. If your bean is RequestScope, change to ViewScope
3) As you are "cascading" from someBean to selectedItem to category, note that the selectedItem and selectedParty must be the same through (not only same content but also same memory adress) or the lifecycle of JSF will stop. I guess you are setting the selected objects earlier. If you are using AJAX, try to post a "full" request to a new page with the drop-down to debug this.
4) To simplify this, you could try to move the category and categories directly into someBean so the page should look like
<p:selectOneMenu value="#{someBean.category}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{someBean.categories}" var="category" itemLabel="#category.name}"/>
</p:selectOneMenu>

Related

How to hide components by selecting a new value on h:selectOneMenu?

I have a page with a h:selectOneMenu, and I want to hide a dataTable when I choose a new value on the h:selectOneMenu while some bean attribute values update. I'm trying diferent things, and I'm able to hide the dataTable by reloading the page. I have tried this using some JavaScript code, on onsomething attributes. In that case, those bean attribute values disapear although I use t:saveState (and I also need the view scope for the bean).
Here is a little example to clarify this:
<h:selectOneMenu id="list" value="#{Bean.id}">
<f:selectItems value="#{Bean.List}" var="element" itemLabel="#{element.name}" itemValue="#{element.id}"/>
<t:saveState value="#{Bean.id}"/>
<f:ajax listener="#{Bean.populateBean}" render=":form:period" event="change"/>
</h:selectOneMenu>
<h:selectOneMenu id="period" value="#{Bean.period}">
<f:selectItems value="#{Bean.listaPeriod}" var="period" itemLabel="#{period[1]}" itemValue="#{period[0]}"/>
<t:saveState value="#{Bean.period}"/>
</h:selectOneMenu>
<h:commandButton id="search" action="#{Bean.doSearch}"></h:commandButton>
<t:dataTable id="data" value="#{Bean.configList}" rendered="#{Bean.search}"
<t:column>
...
</t:dataTable>
And Bean.doSearch changes Bean.search to true. How can I hide the dataTable when I choose a new value on h:selectOneMenu id="list"?
EDIT:
In short, the search button renders the table, and I would want to hide that table when I just choose a new value on the "list" dropdown without reloading the page. Now, the <f:ajax> is used only to rerender the "period" dropdown value.
Thanks in advance!
Your question is hardly understandable, but if you want to kick the datatable out of your view on ajax request triggered by your dropdown, you need to enclose it in a component that's always present in the view so that HSF would know what to rerender when ajax call successfully finishes:
<h:panelGroup id="rerender">
<t:dataTable rendered="#{bean.condition}" ...>
....
</t:dataTable>
<h:panelGroup>
Of course, to get it working you should specify id of the component to be rerendered:
<h:selectOneMenu ...>
...
<f:ajax listener="#{bean.populate}" render="rerender" />
</h:selectOneMenu>
Note that if both dropdown and datatable components belong to the same form, it's not necessary to specify absolute id of the latter's parent. Also note that specifying event is redundant, as it already defaults to the right one. Finally, as you're on JSF 2.x, using save state is not necessary, just bind the values of your form data to a view scoped bean.
The last thing to note is the fact that you fail to conform to the Java Naming Conventions, the thing you should correct as soon as possible.

JSF: setting a property in a wizard form with multiple submit actions

I have this scenario: in the first tab of a primefaces wizard component, I have some fields and a button. The button saves some data, does some business logic and, with the results, sets some properties of the form bean (which is in ViewScope) that are not related to a specific field of the form. I have checked that in the invoke application phase, the values are set properly.
In the second tab I have another button that has to do some business logic using the values set by the first one but, doing some debug, I noticed that the values, even if not related to any field of the form, are overwritten I think during the update model phase invoked when I click the second button.
How can I avoid this? Is there a way to obtain the correct behaviour?
I looked around but I couldn't find any example of a wizard form with multiple submissions. Thanks for help!
<p:wizard widgetVar="wiz" render="true" id="wizard" showNavBar="false">
<p:tab id="step0" title="Step0" step="0">
<!-- Some other fields-->
<p:commandButton value="Save and do some business logic"
action="#{formBean.save}"
oncomplete="wiz.loadStep (wiz.cfg.steps [1], false)">
</p:commandButton>
</p:tab>
<p:tab id="step1" title="Step1" step="1">
<!-- Some other fields-->
<p:commandButton value="Second button: use the previous informations"
action="#{formBean.doSomething}"
oncomplete="wiz.loadStep (wiz.cfg.steps [2], false)">
</p:commandButton>
</p:tab>
</p:wizard>
Edit:
To show an example I can say that my bean contains a business logic object. During the first submission, this object is being saved so the database (Mysql and Hibernate), assigns to it a progressive id.
During the second submission, when I try to read this id, the value is zero so, obviously, I get an error.
Something like:
public class FormBean{
private BLObject object;
// Constructor Getters and setters
// Method executed during the first submission
public void save(ActionEvent actionEvent) {
//Save the object and set his id
PersistanceClass.getInstance().save(object);
}
// Method executed during the second submission
public void doSomethingWhitTheId(ActionEvent actionEvent) {
//Access the id... id=0 returned
int id = object.getId();
}
}
The problem was that, navigating from one page to another, Primefaces wizard, uses a String to identify the tab. Returning the value of this String, which is not null nor void, makes the view bean go out of scope. It is ok when you have a simple form but, if you wanna set some properties not related to a form element during the flow, they get overwritten during the tab changes.
I solved the problem adding to the form an hidden parameter linked to the property I wanted to preserve.
<h:inputHidden value="formBean.idToPreserve" id="inputHidden" />

PrimeFaces f:setPropertyAction with dataTable works only after a row is selected

Here is my Problem:
Backing bean is #SessionScoped
I have a p:dataTable with selectionMode set to single and selection #{bean.selectedCar}
first column in the table is a commandLink that updates a dialog :detailDialog, oncomplete shows the dialog and has an actionListener that hast to clear some property in the bean
f:setPropertyListener has a value #{item} and target #{bean.selectedCar}
I want that the user is able to click the link and the dialog will display the information for the object on the row, but it works only if a row in the dataTable was selected first. If no row was selected and the user clicks on the commandLink NULL is passed to the bean.selectedCar and the page hangs.
As a workaround I would like at least to be able to preselect the first row in the dataTable and hopefully this will do the trick.
You can re-use your f:setPropertyListener in your command link:
<p:commandLink value="show"
actionListener="#{bean.doSomething}"
oncomplete="dialog.show()">
<f:setPropertyListener value="#{item}" target="#{bean.selectedCar}"/>
</p:commandLink>
alternatively (since we are iterating through a dataTable) you can pass the current #{item} object to your <p:commandLink.../>:
<p:commandLink value="Show"
action="#{bean.selectThisCar(item)}"
oncomplete="dialog.show()"/>
Thanks #akoskm for your answer. The first variant was that what I used for the button. My problem was that (as the second option) this method conflicted with the selection option in the dataTable. Removing the selection="#{bean.selectedCar}" from the p:dateTable parameters was the salution of my problem. Thanks for the help.

Primefaces ManyCheckbox inside ui:repeat calls setter method only for last loop

I have a <p:selectManyCheckbox> inside <ui:repeat>, getting it's items from a List of a certain Object Class (provided by <ui:repeat>-variable) and is supposed to save the chosen items into another List of the same Object Class. But it calls the setter method #{cartBean.setSelectedExtras} only for the last entry (last iteration of <ui:repeat>).
<ui:repeat var="item" value="#{category.items}">
<p:selectManyCheckbox id="extraCheckbox" value="#{cartBean.selectedExtras}" layout="pageDirection" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{item.items5}" var="extra" itemLabel="#{extra.name}"/>
</p:selectManyCheckbox>
</ui:repeat>
Update:
I changed the above construct just the way BalusC proposed.
Declaration in backing bean is now:
private List<List<Item>> selectedExtras = new ArrayList<List<Item>>();
When I check checkboxes that were created by the first loops of <ui:repeat> and click the <p:commandButton> inside the same <h:form> the setter method of selectedExtras is not called. When I check the checkboxes created in the last loop of <ui:repeat> and click the <p:commandButton> I get an Exception:
javax.el.PropertyNotFoundException: /lightbox-item.xhtml #57,165 value="#{cartBean.selectedExtras[iteration.index]}": null
This construct works fine for me.
As mentioned in among others the showcase page, the omnifaces.SelectItemsConverter uses by default the toString() representation of the complex object as converted item value. So if you didn't override the toString() method (so that it still defaults to com.example.SomeClass#hashcode which changes on every instantiation) and the #{item} managed bean is request scoped, then the list would basically be changing on every HTTP request. This would cause a "Validation Error: Value is not valid" error.
If you add
<p:messages autoUpdate="true" />
or
<p:growl autoUpdate="true" />
so that you get all (missing) validation/conversion messages in the UI, then you should have noticed it.
In order to utilize the omnifaces.SelectItemsConverter at its best, you should override the toString() method accordingly so that it returns a fixed and unique representation of the complex object. E.g.
#Override
public String toString() {
return "Extra[id=" + id + "]";
}
Alternatively, you could put the #{item} managed bean in a broader scope, such as the view scope.
Update as to your update, you're binding the selected values of all checkboxgroups to one and same bean property #{cartBean.selectedExtras}. This way every iteration overrides the property with the values from the current iteration round as long as until you end up with the values of the last iteration. If you've placed a debug breakpoint on the setter, you'd have noticed that.
This is not right. They should each point to a different bean property. Technically, you should have a #{item.selectedExtras} as property. But I think that this makes no sense in your current design. Better would be to make the #{cartBean.selectedExtras} an List<Item[]> or Item[][]. This way you can get them to set based on the iteration index as follows:
<ui:repeat var="item" value="#{category.items}" varStatus="iteration">
<p:selectManyCheckbox id="extraCheckbox" value="#{cartBean.selectedExtras[iteration.index]}" layout="pageDirection" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{item.items5}" var="extra" itemLabel="#{extra.name}"/>
</p:selectManyCheckbox>
</ui:repeat>
In case of List<Item[]> you only need to make sure that you preinitialize selectedExtras with nulls as many times as there are #{category.items} in bean's (post)constructor.
for (Item item : category.getItems()) {
selectedExtras.add(null);
}
In case of Item[][], you can suffice with
selectedExtras = new Item[category.getItems().size()];

h:selectOneMenu default item to null, clear empty item value, required field

Hello I'm having trouble with the following piece of code:
<h:selectOneMenu id="selectTipoAutorizacion"
value="#{autorizacion.codigoTipoAutorizacion}"
required="true">
<f:selectItems
value="#{cc.attrs.controller.getListaTiposAutorizacion(autorizacion)}"
var="tipoAutorizacion"
itemLabel="#{tipoAutorizacion.nombreTipoAutorizacion}"
itemValue="#{tipoAutorizacion.id.codigoTipoAutorizacion}" />
<a4j:ajax event="change" execute="#this"
listener = #{myListener.listener} render="selectAutorizador" />
</h:selectOneMenu>
The problem is that the default selected value is always the first one of the tag. And that's bothering the users, cause some data is loaded based on the selected item value..., however that info it's not loaded until the change event occurs (a4j:ajax tag), so right now the user has to select another item, and then select the previous one in order to see the default's item related info.
I addressed the problem by loading the default's item related info at the beginning, however the user doesn't like this. Because it could lead to confusion. So the question is... how could I avoid that behaviour? What I want is the selectOneMenu to load with a clear value( Like if there weren't any f:selectItems). Thanks a lot.
Your field is required, there should be nothing valid to default to in this case. Add an empty selectItem to the top of the list: selectItems.add(0, new SelectItem("", "")); or this way: <f:selectItem itemValue="" itemLabel="" />
By default it would then select the empty selectItem.
The user will be forced to make a choice as the required="true" does not allow an empty selection.
Just preload the desired data in the (post)constructor of the bean.
if (codigoTipoAutorizacion != null) {
// Preload all other desired data as well.
}
Add an f:selectItem before the f:selectItems, with an empty value, or something like "please select ...", and mark it as non-selectable.

Resources