Hi I have built two consecutive dropdowns one being initialized in init() method and the other being populated on the basis on first one's selection.
Command Button is used to use both the selected values for processing, Strange that Setter for the selected values is being called twice(setting blank the second time..as I found out in debugging)..I need understand whyu I am getting blank values?
myPage.xhtml
<p:selectOneMenu id="dbList"
value="#{backingBean.selectedConnection}"
style="width:200px">
<p:ajax listener="#{backingBean.onConnectionChange()}"
update="aliasList" />
<f:selectItem itemLabel="Select" itemValue="#{backingBean.selectedConnection}"
noSelectionOption="false" />
<f:selectItems value="#{backingBean.connectionsTypeList}" />
</p:selectOneMenu>
<p:outputLabel for="aliasList" value="Connection Alias" />
<p:selectOneMenu id="aliasList"
value="#{backingBean.aliasSelected}" style="width:200px">
<f:selectItem itemLabel="Select" itemValue="#{backingBean.aliasSelected}"
noSelectionOption="false"/>
<f:selectItems value="#{backingBean.connectionsList}" />
</p:selectOneMenu>
<p:commandButton value="myButton"
actionListener="#{backingBean.myMethod()}"
id="myButton" />
backingBean.java
#ManagedBean
#ViewScoped
public class BackingBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ArrayList<String> connectionsList;
private String aliasSelected;
private String selectedConnection;
private ArrayList<String> connectionsTypeList;
#PostConstruct
public void init() {
// Initialize the connection alias drop down here
this.connectionsTypeList = getConnectionTypeList();
this.connectionsList=new ArrayList<String>();
}
public void onConnectionChange() {
System.out.println("In Connection Change, populating aliases");
getConnectionDBList();
}
public String getAliasSelected() {
return aliasSelected;
}
//Dont know why this is called twice on button click and second time value set is blank
public void setAliasSelected(String aliasSelected) {
this.aliasSelected = aliasSelected;
}
public ArrayList<String> getConnectionsList() {
return connectionsList;
}
public void setConnectionsList(ArrayList<String> connectionsList) {
this.connectionsList = connectionsList;
}
public String getSelectedConnection() {
return selectedConnection;
}
public void setSelectedConnection(String selectedConnection) {
this.selectedConnection = selectedConnection;
}
public ArrayList<String> getConnectionsTypeList() {
return connectionsTypeList;
}
public void setConnectionsTypeList(ArrayList<String> connectionsTypeList) {
this.connectionsTypeList = connectionsTypeList;
}
public void myMethod() {
System.out.println("selected Alias is "+aliasSelected);
System.out.println("selected connection is "+selectedConnection);
}
}
New to primefaces,please help
Answer to this :"setting blank the second time..as I found out in debugging"
This is happening because I realized I was pointing the same property to two ui components..as in..two dropdowns in the same page were linked to the same property of the backing bean..Sillyh Mistake..hence the second call to the setter..with Blank values..as it was not initialized for the second UI Component
Related
After my post (Hibernate Primefaces AutoComplete text) I switched the implementation a little. But now I am facing a problem.
Even if I use an AJAX event I do not keep the selected value saved to populate a second drop down.
my CREATE.XHTML
<h:head></h:head>
<ui:debug rendered="true"/>
<body>
<h:form id="createAddressForm" prependId="true">
<!-- <p:messages autoUpdate="true" /> -->
<p:growl id="msgs" showDetail="true" />
<h:panelGrid columns="2" style="margin-bottom:10px" cellpadding="5">
<p:outputLabel for="countryDropDown" value="Country" />
<p:selectOneMenu id="countryDropDown" value="#{addressController.selectedIsoCountry}" >
<p:ajax listener="#{addressController.onCountryChange}" update="stateDropDown" />
<f:selectItem itemValue="" itemLabel="Select a country"/>
<f:selectItems value="#{addressController.countryMap}" />
</p:selectOneMenu>
<p:outputLabel for="stateDropDown" value="State" />
<p:selectOneMenu id="stateDropDown" value="#{addressController.state}" >
<f:selectItem itemValue="" itemLabel="Selecione a State" />
<f:selectItems value="#{addressController.stateMap}" />
</p:selectOneMenu>
</h:panelGrid>
</h:form>
</body>
</html>
And this is AddressController.java
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.SessionScoped;
import javax.inject.Named;
import br.com.azulseguros.ejb.CountryEJB;
import br.com.azulseguros.entity.Country;
import br.com.azulseguros.entity.State;
#SessionScoped
#Named
public class AddressController {
#EJB
private CountryEJB countryEJB;
private String selectedIsoCountry = null;
private State state = null;
private Map<String, String> countryMap = null;
private Map<String, String> stateMap = null;
#PostConstruct
private void init() {
Map<String, String> retorno = new TreeMap<String, String>();
for (Country _tmp : countryEJB.findAll()) {
retorno.put(_tmp.getName(), _tmp.getIso());
}
countryMap = retorno;
}
public Map<String, String> getCountryMap() {
return countryMap;
}
public Map<String, String> getStateMap() {
return stateMap;
}
public String getSelectedIsoCountry() {
return selectedIsoCountry;
}
public State getState() {
return state;
}
public void setSelectedIsoCountry(String selectedIsoCountry) {
this.selectedIsoCountry = selectedIsoCountry;
}
public void setState(State state) {
this.state = state;
}
public void setCountryMap(Map<String, String> countryMap) {
this.countryMap = countryMap;
}
public void setStateMap(Map<String, String> stateMap) {
this.stateMap = stateMap;
}
public void onCountryChange() {
setStateMap(getStatesFromSelectedCountry());
}
private Map<String, String> getStatesFromSelectedCountry() {
Map<String, String> retorno = new TreeMap<String, String>();
if (selectedIsoCountry != null && !selectedIsoCountry.equals("")) {
for (State _tmp : countryEJB.findByIso(selectedIsoCountry).getStates()) {
retorno.put(_tmp.getName(), _tmp.getFu());
}
}
return retorno;
}
}
The EJB responsibile for finding all countries and states is working fine. There is a lot of issues with that and I do not know what to do to fix it.
1 - After I invoke the page for the first time it calls the init method 10 times;
2 - After that it evoked the method getStatesFromSelectedCountry even not choosing any country from the 1st drop down and after that evokes the init method again;
3 - When i choose a country it evokes 7 times the init method and then the getStatesFromSelectedCountry() but the selectedIsoCountry is null.
The bean's init method is invoked many times because you have defined the bean both as a CDI bean using javax.inject.Named, without scope, and as a JSF Managed Bean using javax.faces.bean.SessionScoped; if you intended to use CDI beans, simply replace the latter annotation with javax.enterprise.context.SessionScoped. See Why are there different bean management annotations
From a CDI point of view, the bean is by default RequestScoped, this should explain also the second issue you are experiencing.
Concerning the third issue, see this Q/A:
Why is the getter called so many times by the rendered attribute?
Why JSF calls getters multiple times
I have a List<String> and I successfully represent it in a datatable; now I am trying to create a composite component out of it, but it seems I keep having trouble understanding how StateHelper does work.
What I want to do is, if the value attribute passed by xhtml evaluates to null, to create a new List<String> automatically. Right now, the only possible action is clicking a button that adds a new item to the list.
My component
<cc:interface componentType="testComponent">
<cc:attribute name="value" required="true" type="java.util.List"/>
</cc:interface>
<cc:implementation>
<f:event type="postAddToView" listener="#{cc.init}" />
<p:dataTable id="data" value="#{cc.data}" var="_data">
<p:column headerText="Nombre / Relación">
<h:outputText value="#{_data}" />
</p:column>
</p:dataTable>
<p:commandButton value="Añadir" process="#this" update="data"
actionListener="#{cc.addData}" ajax="true"/>
</cc:implementation>
The component bean is
#FacesComponent("testComponent")
public class TestComponent extends UIOutput implements NamingContainer {
private static final String LISTA_DATOS = "LST_DATOS";
private static final Logger log = Logger.getLogger(TestComponent.class.getName());
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public List<String> getData() {
#SuppressWarnings("unchecked")
List<String> data = (List<String>) this.getStateHelper().get(LISTA_DATOS);
return data;
}
public void setData(List<String> data) {
this.getStateHelper().put(LISTA_DATOS, data);
}
public void addData() {
List<String> data = (List<String>)this.getData();
data.add("HOLA");
this.setData(data);
}
public void init() {
log.info("En init()");
if (this.getStateHelper().get(LISTA_DATOS) == null) {
if (this.getValue() == null) {
this.getStateHelper().put(LISTA_DATOS, new ArrayList<String>());
} else {
this.getStateHelper().put(LISTA_DATOS, this.getValue());
}
}
}
The component is called like that
<h:form>
<imas:editorTest value="#{testBean.data1}"/>
</h:form>
<h:form>
<imas:editorTest value="#{testBean.data2}"/>
</h:form>
with testBean being:
private List<String> data1 = new ArrayList<>(Arrays.asList("ONE", "TWO", "SIXTYNINE"));
private List<String> data2 = null;
public List<String> getData1() {
return this.data1;
}
public void setData1(List<String> data1) {
this.data1 = data1;
}
public List<String> getData2() {
return this.data2;
}
public void setData2(List<String> data2) {
this.data2 = data2;
}
The issue I found is that, when passing data2 (the null list), clicking the button adds a new item but only the first two times; after that, no matter how many times I click the button, no new items are added to the list (no exception shown in the log). On the opposite, there is no problem adding as many items as I wish to the component initialized with data1.
One thing that I have observed and that leads me to thinking that I am misusing getStateHelper is that, when I click the button, the init() method is executed twice and, at that time, this.getStateHelper().get(LISTA_DATOS) is null, while I expected it to be not null due to having initialized it when the component was first rendered. I expected getStateHelper to carry such state between invocations, where am I wrong?.
Oh! I am using Wildfly 8.1 (no upgrades) with JDK 7.
Digging deeper, I found some evidence of a bug, so I reported it. I'll update the answer to see if it is really a bug or some big misconception from me.
My composite components share values stored in StateHelper when placed inside PrimeFaces DataTable. The most of examples about keeping component state that I've seen suggest to use getStateHelper().put()/eval() methods of UINamingContainer. I do use these methods but without luck. How to do that properly? (currently I use workaround described in the end of this post)
To illustrate the issue I've created click counter based on PrimeFaces commandLink component. In the example below two counters that are outside of dataTable work as expected. But all counters that appear inside dataTable share the same counter value (clicking on any one of them continues common value).
Update:
I've figured out that to allow sorting (for example) to work correctly inside datatable I need to bind my component to certain raw somehow. And "shared" state helper allows to do exactly that. So now I specify row key as an attribute and have updated methods to store state. There is no question if this way is correct.
Update for counterLink.xhtml:
<composite:interface componentType="CounterLink2Component">
<composite:attribute name="key" type="java.io.Serializable"/>
</composite:interface>
And CounterLinkComponent.java now is:
#FacesComponent("CounterLinkComponent")
public class CounterLinkComponent extends UINamingContainer {
private enum PropertyKeys {
COUNTER_VALUE
}
public void count() {
storeInstanceValue(PropertyKeys.COUNTER_VALUE.toString(), getCounterValue() + 1);
}
public Integer getCounterValue(){
return (Integer) evalInstanceValue(PropertyKeys.COUNTER_VALUE.toString(), 0);
}
private Serializable getKeyAttr() {
return (Serializable) getAttributes().get("key");
}
private void storeInstanceValue(String key, Object value) {
Serializable subkey = getKeyAttr();
if (subkey == null) {
getStateHelper().put(key, value);
} else {
getStateHelper().put(subkey, key, value);
}
}
private Object getInstanceValue(String key) {
Serializable subkey = getKeyAttr();
if (subkey == null) {
return getStateHelper().eval(key);
} else {
return ((Map) getStateHelper().eval(subkey, Collections.emptyMap())).get(key);
}
}
private Object evalInstanceValue(String key, Object _default) {
Object result = getInstanceValue(key);
return result != null ? result : _default;
}
}
Original example:
Primefaces 5.0, Glassfish 4.
counterLink.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui">
<composite:interface componentType="CounterLinkComponent">
</composite:interface>
<composite:implementation>
<p:commandLink action="#{cc.count()}" partialSubmit="true" update="#this">
<h:outputText value="#{cc.counterValue}"/>
</p:commandLink>
</composite:implementation>
</html>
CounterLinkComponent.java:
import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import java.io.Serializable;
#FacesComponent("CounterLinkComponent")
public class CounterLinkComponent extends UINamingContainer {
private enum PropertyKeys {
COUNTER_VALUE
}
public void count() {
getStateHelper().put(PropertyKeys.COUNTER_VALUE, getCounterValue() + 1);
}
public Integer getCounterValue(){
return (Integer) getStateHelper().eval(PropertyKeys.COUNTER_VALUE, 0);
}
}
Usage example:
<h:form>
<p:panelGrid columns="1">
<cmp:counterLink/>
<cmp:counterLink/>
<p:dataTable var="item" value="#{counterLinkStoreBean.itemList}">
<p:column headerText="Name">
#{item.name}
</p:column>
<p:column headerText="Counter">
<cmp:counterLink/>
</p:column>
</p:dataTable>
</p:panelGrid>
</h:form>
Backing bean for this example (just creates several items):
#Named
#ViewScoped
public class CounterLinkStoreBean implements Serializable {
private List<Item> itemList;
#PostConstruct
private void init() {
itemList = new ArrayList<Item>();
itemList.add(new Item("Test 1"));
itemList.add(new Item("Test 2"));
itemList.add(new Item("Test 3"));
}
public List<Item> getItemList() {
return itemList;
}
public static class Item {
private final String name;
public Item(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
In my case I can use workaround storing values in a map with component clientId as a secondary key:
private void storeInstanceValue(Serializable key, Object value) {
getStateHelper().put(key, getClientId(), value);
}
private Object getInstanceValue(Serializable key) {
return ((Map)getStateHelper().eval(key, Collections.emptyMap())).get(getClientId());
}
Is there more natural solution?
I'm trying to implement on JSF data table a binding to a backed bean value with a nested data table with a binding to a value of the current data row, see this code:
<h:form prependId="false">
<h:dataTable binding="#{tableBean.mainDataTable}" var="row" >
<h:column>
<h:dataTable binding="#{row.nestedDataTable}" />
</h:column>
</h:dataTable>
</h:form>
And these are the backed beans and data:
#ManagedBean
#SessionScoped
public class TableBean implements Serializable{
private HtmlDataTable mainDataTable;
private List<TableBeanData> tableBeanDataLst;
public TableBean() {
tableBeanDataLst = new ArrayList<TableBeanData>();
DataModel<TableBeanData> mainDataModel =new ListDataModel<TableBeanData>(tableBeanDataLst);
mainDataTable = new HtmlDataTable();
mainDataTable.setValue(mainDataModel);
}
public HtmlDataTable getMainDataTable() {
return mainDataTable;
}
public void setMainDataTable(HtmlDataTable mainDataTable) {
this.mainDataTable = mainDataTable;
}
public List<TableBeanData> getTableBeanDataLst() {
return tableBeanDataLst;
}
public void setTableBeanDataLst(List<TableBeanData> tableBeanDataLst) {
this.tableBeanDataLst = tableBeanDataLst;
}
}
public class TableBeanData implements Serializable{
private HtmlDataTable nestedDataTable;
public TableBeanData (){
nestedDataTable = new HtmlDataTable();
}
public HtmlDataTable getNestedDataTable() {
return nestedDataTable;
}
public void setNestedDataTable(HtmlDataTable nestedDataTable) {
this.nestedDataTable = nestedDataTable;
}
}
But I get a property not found exception:
/index.xhtml #17,69 binding="#{row.nestedDataTable}": Target Unreachable, indentified 'row' resolved to null
I don't understand well, because I'm Initializing the datatables in the constructors.
How can I resolve this type of error to allow this configuration of table, nested table and data bindings?
I'm still trying to program an app with jsf2, ejb3.1, cdi and glassfish.
I have a form which have a selectOneMenu
<h:form prependId="false">
...
<f:validateBean>
...
<h:selectOneMenu value="#{bottleManagedBean.selectProducer}" id="selectproducerb"
validatorMessage="#{messages.bottleaddinvalideproducer}" immediate="true">
<f:converter binding="#{producerConverter}"/>
<f:selectItems value="#{bottleManagedBean.producerItems}" />
</h:selectOneMenu>
...
The of the selectItems component are well showned but when I submit the form the value of bottleManagedBean.selectProducer is always null.
My formbean
#Named("bottleManagedBean")
#RequestScoped
public class BottleManagedBean {
....
private List<Producer> producers;
public List<Producer> getProducers() {
if (producers == null) {
setProducers(producerService.list(Producer.class));
}
return producers;
}
public void setProducers(List<Producer> producers) {
this.producers = producers;
}
private Producer selectProducer;
public Producer getSelectProducer() {
return selectProducer;
}
private List<SelectItem> producerItems;
public List<SelectItem> getProducerItems() {
if (producerItems == null) {
producerItems = new ArrayList<SelectItem>();
for (Producer current : getProducers()) {
producerItems.add(new SelectItem(current.getId(), current.getName()));
}
}
return producerItems;
}
public void setProducerItems(List<SelectItem> producerItems) {
this.producerItems = producerItems;
}
...
The converter
#Named("producerConverter")
public class ProducerConverter implements Converter {
#Inject
BusinessService<Producer> service;
private static Logger trace = Logger.getLogger(ProducerConverter.class.getCanonicalName());
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String id) {
try {
return service.findByID(Producer.class, Integer.parseInt(id));
} catch (NumberFormatException e) {
e.printStackTrace();
throw new ConverterException(e);
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object o) {
String asString = null;
if (o != null) {
asString = String.valueOf(o);
}
return asString;
}
}
I tried to debug the app .When I submit the form , the application never goes to the setter.
I also tried to add a valuechangelistener and my app never called this method.
Finally, I tried to submit an integer (eg. the id of my bean) and the value is well filled when I submit.1
So,... what's wrong ?
Thanks in advance for your help
here is how you should use the valuechangelistener properly in jsf2
Add <f:ajax listener="#{bottleManagedBean.selectProducer}" /> to your <h:selectOneMenu
Like this:
<h:selectOneMenu value="#{bottleManagedBean.selectProducerValueChange}" id="selectproducerb"
validatorMessage="#{messages.bottleaddinvalideproducer}">
<f:ajax listener="#{bottleManagedBean.selectProducerValueChange}" />
<f:converter binding="#{producerConverter}"/>
<f:selectItems value="#{bottleManagedBean.producerItems}" />
</h:selectOneMenu>
and implement the selectProducerValueChange in your bean...