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
Related
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
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 have a p:dataTable with p:contextMenu and some p:menuitems. One of these menu items should pass on an id to another view which is read like pointed out here.
The thing is the converter throws the required message like the id wasn't sent - and it seems like it truly isn't. I think I'm missing something basic, but I really couldn't figure it out. Here's the code:
The source view
<h:form id="formTabela">
<p:fieldset>
<p:dataTable id="sistemas"
selection="#{sistemaMb.sistemaSelecionado}">
(...)
</p:dataTable>
</p:fieldset>
<p:contextMenu for="sistemas">
<p:menuitem value="Gerenciar módulos" icon="ui-icon-search"
action="modulos?faces-redirect=true&includeViewParams=true"
ajax="false">
<f:param name="id" value="#{sistemaMb.sistemaSelecionado.id}"/>
</p:menuitem>
(More items...)
</p:contextMenu>
(Some dialogs...)
</h:form>
Target view (modulos)
<!-- This is on body: -->
<ui:define name="metadata">
<f:metadata>
<f:viewParam name="id" value="#{moduloMb.sistema}"
converterMessage="foo"
required="true"
requiredMessage="bar"/>
<f:event type="preRenderView" listener="#{moduloMb.init()}" />
</f:metadata>
</ui:define>
Target view managed bean
#ManagedBean
#ViewScoped
public class ModuloMb implements Serializable {
private Sistema sistema;
#PostConstruct
public void init() {
if (!Faces.isPostback() && !Faces.isValidationFailed()) {
// business stuff, but "sistema" is always null.
}
}
public Sistema getSistema() {
return sistema;
}
public void setSistema(Sistema sistema) {
this.sistema = sistema;
}
(...)
}
The converter
#FacesConverter(forClass = Sistema.class)
public class SistemaConverter implements Converter {
private final SistemaService sistemaService = lookup(SistemaService.class);
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
if (value == null || !value.matches("\\d+")) {
return null;
}
Optional<Sistema> optSistema = sistemaService.find(Short.valueOf(value));
if (!optSistema.isPresent())
throw new ConverterException(
new FacesMessage("Id de sistema inválido " + value));
return optSistema.get();
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
if (!(value instanceof Sistema) || ((Sistema) value).getId() != null) {
return null;
}
return ((Sistema) value).getId().toString();
}
}
The includeViewParams directive which you use in the outcome string for the p:menuitem tells JSF to insert the special set of view parameters as request parameters in the GET request to your target view.
BUT, f:param does not define a view param and neither will it be appended to the URL to which the GET request is done.
It does work if you append the parameter directly to the implicit navigation outcome: "modulod?faces-redirect=true&id=..."
i have a datatable with one row , i need to edit the fields of this row so i have a few inputText with the values, but when i edit them and click on the commandbutton(that calls the method "actualizarUsuario" the values are passed as null.
this is my bean code:
#ManagedBean(name = "user")
#ViewScoped
public class userDetalles implements Serializable {
private Usuario u;
private usuarioController controlador;
Rol rol;
private long selection;
private long selectionrol;
Agrupacion agrupacion;
private Privilegio privilegio;
private RolController controladorRol;
private ControladorAgrupaciones controladorAgrup;
private String nombres;
private String apellidoP;
private String apellidoM;
private Boolean check;
#PostConstruct
public void init() {
rol= new Rol() ;
u=new Usuario();
agrupacion=new Agrupacion();
privilegio=new Privilegio();
controlador= new usuarioController();
controladorRol=new RolController();
controladorAgrup=new ControladorAgrupaciones();
Usuario u=new Usuario();
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
//Obtener parametros del request
Map<String, String> parameterMap = (Map<String, String>) externalContext.getRequestParameterMap();
long iduser = Long.valueOf(parameterMap.get("id_usuario"));
this.u=controlador.getUser(iduser);
}
public Usuario getU() {
return u;
}
public void setU(Usuario u) {
this.u = u;
}
public long getSelection() {
System.out.println("selection value----------->"+selection);
return selection;
}
public void setSelection(long selection) {
this.selection = selection;
}
public long getSelectionrol() {
return selectionrol;
}
public void setSelectionrol(long selectionrol) {
this.selectionrol = selectionrol;
}
public String getNombres() {
return nombres;
}
public void setNombres(String nombres) {
this.nombres = nombres;
}
public String getApellidoP() {
return apellidoP;
}
public void setApellidoP(String apellidoP) {
this.apellidoP = apellidoP;
}
public String getApellidoM() {
return apellidoM;
}
public void setApellidoM(String apellidoM) {
this.apellidoM = apellidoM;
}
public Boolean getCheck() {
return check;
}
public void setCheck(Boolean check) {
this.check = check;
}
public void actualizarUsuario(){
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
Map<String, String> parameterMap = (Map<String, String>) externalContext.getRequestParameterMap();
nombres=parameterMap.get("nombres");
apellidoP=parameterMap.get("apellidoP");
apellidoM=parameterMap.get("apellidoM");
check=Boolean.parseBoolean(parameterMap.get("check"));
//test
System.out.println(nombres+" "+apellidoP+" "+apellidoM+" "+check);
u.setNombres(nombres);
u.setApellidoPaterno(apellidoP);
u.setApellidoMaterno(apellidoM);
u.setActive(check);
controlador.saveUsuario(u);
}
}
and this is my view:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<div class="container">
<h:panelGroup id="Users">
<h:form id="Form">
<h2>Detalles Usuario</h2>
<h:dataTable id="users" value="#{user.u}" styleClass="table table-striped table-bordered" headerClass="sorting_asc"
rowClasses="odd,even">
<h:column>
<f:facet name="header">#</f:facet>
#{user.u.id}
</h:column>
<h:column>
<f:facet name="header">Identificador</f:facet>
<h:inputText id="identificador" value="#{user.u.identificador}" />
</h:column>
<h:column>
<f:facet name="header">Nombre</f:facet>
<h:inputText id="nombres" value="#{user.u.nombres}"/>
<h:inputText id="apellidoP" value="#{user.u.apellidoPaterno}"/>
<h:inputText id="apellidoM" value="#{user.u.apellidoMaterno}"/>
</h:column>
<h:column>
<f:facet name="header">Active</f:facet>
<h:selectBooleanCheckbox id="check" value="#{user.u.active}"></h:selectBooleanCheckbox>
</h:column>
</h:dataTable>
<h:commandButton value="Actualizar" type="submit" styleClass="btn-primary" actionListener="#{user.actualizarUsuario}">
</h:commandButton>
</h:form>
<script type="text/javascript" src="js/paging-bootstrap.js"></script>
<script type="text/javascript" src="js/contenidoc.datatable.init.js"></script>
</h:panelGroup>
</div>
</ui:composition>
Your concrete problem is caused because you used the wrong parameter names. Look in the generated HTML output and the HTTP traffic monitor for the right parameter names.
However, your actual problem is bigger: your view/model approach is completely wrong. You shouldn't be using a <h:dataTable> at all. It is intented for a collection of entities like List<User>, not for a single entity like User. You should be using <h:panelGrid>. You don't need to explode/flatten model properties in controller at all. You have those properties already in the model itself. You don't need to manually traverse the request parameter map. JSF will already do all the job for you.
I won't rewrite this mess for you, but to the point you should follow the following kickoff example:
Model:
public class User {
private Long id;
private String username;
private String firstname;
private String lastname;
// ...
// Autogenerate standard getters/setters.
}
Controller:
#ManagedBean
#ViewScoped
public class EditUser {
private User user; // Initialize it in postconstruct or as viewparam.
private UserService service; // Initialize it as #EJB or in postconstruct.
public void save() {
service.save(user); // That's all. Really.
}
public User getUser() {
return user;
}
// No other getters/setters! They are all already in User class.
}
View:
<h:panelGrid>
<h:inputText value="#{editUser.user.username}" />
<h:inputText value="#{editUser.user.firstname}" />
<h:inputText value="#{editUser.user.lastname}" />
<h:commandButton value="save" action="#{editUser.save}" />
</h:panelGrid>
That's all. See also among others this JSF 2.0 tutorial. As to your attempt to get the user by ID, you should rather use <f:viewParam>, see also What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for? and communication in JSF 2.0.
I'm implementing a PrimeFaces picklist in a dialog. Everytime the dialog is shown the contents of the target of the picklist should change depending on a list entry shown before. Before I open the dialog I fill the target of the picklist in ProdukteBean.onEditProdukt(..) with the appropriate values. Unfortunately these target values do not show up in the target container. Here are the relevant code pieces:
list.xhtml:
<p:commandLink id="editButton" update=":dialogForm:editDialogPanel" title="Edit"
oncomplete="produktDialog.show();"
process="#this" partialSubmit="true"
action="#{produkteBean.onEditProdukt}">
<h:outputText value="Bearbeiten" />
<f:setPropertyActionListener value="#{p}" target="#{produkteBean.produkt}" />
</p:commandLink>
dialog.xhtml:
<!-- ... -->
<p:dialog id="dialog" header="Produkt-Details" widgetVar="produktDialog" appendToBody="true" showEffect="explode" hideEffect="explode" modal="true" width="500">
<p:messages id="msgs"/>
<h:form id="dialogForm">
<!-- ... -->
<p:pickList id="produkteDatenList" var="proddat" value="#{produkteBean.datenList}"
itemLabel="#{proddat.bezeichnung}" itemValue="#{proddat}"
converter="produktDatConverter"/>
<!-- ... -->
</h:form>
</p:dialog>
ProdukteBean.java:
#Named("produkteBean")
#ViewScoped // #SessionScoped // #ViewScoped
public class ProdukteBean implements Serializable {
#Inject #Transient private ProdukteService produkteService;
#Inject #Transient private DatenService datenService;
#Inject()
private ProdukteDatenBean produkteDatenBean;
private DualListModel<Dat> datenList = new DualListModel<Dat>();
private Dat dat = null;
public ProdukteBean() {
}
#PostConstruct
private void init() {
getAll();
}
private void getAll() {
logger.debug("getAll()");
getAllProdukte();
getAllDaten();
}
private void getAllDaten() {
logger.debug("getAllDaten()");
List<Dat> source = new ArrayList<Dat>();
source.addAll(datenService.list());
List<Dat> target = new ArrayList<Dat>();
if (produkt.getDaten() != null) {
logger.debug("adding " + produkt.getDaten().size() + " daten to produkt " + produkt.getName());
target.addAll(produkt.getDaten());
}
DualListModel<Dat> datenList = new DualListModel<Dat>();
datenList.setSource(source);
datenList.setTarget(target);
setDatenList(datenList);
}
public List<Produkt> getAllProdukte() {
logger.debug("getAllProdukte()");
return produkteService.list();
}
public void onEditProdukt() {
onEditProdukt(null);
}
public void onEditProdukt(ActionEvent actionEvent) {
logger.debug("onEditProdukt: " + ReflectionToStringBuilder.toString(produkt));
if (produkt != null) {
setSelectedEinheit(produkt.getEinheit());
getAllDaten();
}
FacesMessage msg = new FacesMessage("Produkt ausgewählt", produkt.getName());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
/**
* #return the einheitList
*/
public List<Einheit> getEinheitList() {
if (einheitList == null) {
einheitList = produkteService.getEinheiten();
}
return einheitList;
}
}
ProduktDatConverter.java:
#FacesConverter(forClass=Dat.class,value="produktDatConverter")
#ViewScoped
public class ProduktDatConverter implements Converter {
#Inject
#Transient DatenService datenService;
#Transient
protected final Logger logger = Logger.getLogger(getClass().getName());
// gets never called (of course)
public Object getAsObject(FacesContext arg0, UIComponent arg1, String str) {
logger.debug("getAsObject(): " + str);
return null;
}
public String getAsString(FacesContext arg0, UIComponent arg1, Object object) {
if (object instanceof Dat) {
// logger.debug(" returning id " + String.valueOf(((Dat) object).getId()));
return Long.toString(((Dat) object).getId());
}
return null;
}
}
Any ideas? Many thanks in advance!
You are using #ViewScoped along CDI Managed Bean. Change it to JSF Managed Bean. Better, use CODI instead
Also see:
View Scope in CDI Weld
CDI missing #ViewScoped and #FlashScoped
I suppose it's converter + injection problem. Please, click here and refer to the BalusC answer. You can also try to replace:
#FacesConverter(forClass=Dat.class,value="produktDatConverter")
#ViewScoped
with
#ManagedBean
#RequestScoped
and call the converter like this:
converter="#{produktDatConverter}"