I have a form like this:
<h:form>
Phone number:
<h:inputText id="phone_number"
value="#{dbUserManager.phoneNumber}"
validatorMessage="The phone number is invalid."
required="#{auth.value eq 'MOBILE'}"
requiredMessage="Phone number is required." />
</h:inputText>
Authentication type:
<h:selectOneMenu value="#{dbUserManager.authentication}" converter="#{authenticationTypeConverter}" binding="#{auth}">
<f:selectItems value="#{dbUserManager.authTypeList}" />
</h:selectOneMenu>
</h:form>
AuthenticationType is an entity similar to this:
#Entity
public class AuthenticationType implements Serializable
{
#Id
#Basic(optional = false)
private Short id;
#Basic(optional = false)
private String description;
#Override
//...
public String toString()
{
return description;
}
and finally AuthenticationTypeConverter:
#Stateless
#ManagedBean
#FacesConverter(value="authenticationTypesConverter")
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public class AuthenticationTypeConverter implements Converter
{
#PersistenceContext(unitName="pu")
private EntityManager em = null;
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String string)
{
TypedQuery<AuthenticationType> q = em.createNamedQuery(
AuthenticationType.FIND_BY_DESCRIPTION,
AuthenticationType.class);
q.setParameter("description", string);
return q.getSingleResult();
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object o)
{
if (o == null)
{
return "";
}
return o.toString();
}
}
What I tried to do is that to set the required of phone number to true when authentication type is set to 'MOBILE'. But it's not behaving as I expect.
I think the problem lies in the fact that I need to use FacesConverter for AuthenticationType. But I don't know how to solve it.
Can anyone give a hint?
Thanks
You have to rerender the h:inputText-field after the user chose from your h:selectOneMenu. To do so, change this selection to
<h:selectOneMenu value="#{dbUserManager.authentication}"
converter="#{authenticationTypeConverter}" binding="#{auth}">
<f:selectItems value="#{dbUserManager.authTypeList}" />
<f:ajax event="change" render="phone_number" />
</h:selectOneMenu>
Also in the inputTexts required-attribute I would expect something more like
{dbUserManager.authentication.description}
Give it a try...
Update 2014/01/22:
If you want to hold already typed informations in the phone_number field, you can tell the <f:ajax> tag to execute not only selectOneMenu but also the input field before rendering it new. In this case replace the upper <f:ajax /> solution with
...
<f:ajax event="change" execute="#this phone_number" render="phone_number" />
...
Related
I have 3 managebean one model and 2 controllers (1 for the popup text) i want a popup after i insert into the database.
my page is like this the problem is that when i call for mensaje.mostrar() i works but when i try to call cursoControlador.registrarCurso() and from there try to call mensaje.mostrar() that changes the value from mostrarMensaje (boolean) don't show the popup.
<ui:define name="centro">
<h:form>
<h:panelGrid columns="3" cellpadding="5">
<p:outputLabel value="Nombre" />
<p:inputText value="#{cursoModelo.nombre}"/>
this is the button use and works fine
<p:commandButton action="#{mesaje.mostrar()}" value="Registrar Curso" >
<f:ajax render="#form"/>
</p:commandButton>
and this is the behavior i want
<p:commandButton action="#{cursoControlador.registrarCurso()}" value="Registrar Curso" >
<f:ajax render="#form"/>
</p:commandButton>
and this is the popup message i want to show
</h:panelGrid>
<h:panelGroup layout="block" styleClass="fondo-popup" rendered="#{mensaje.mostrarMensaje}">
<div class="panel-popup">
<p:outputLabel value="#{mensaje.texto}"/>
<h:commandButton value="Aceptar" action="#{mensaje.esconder()}">
<f:ajax render="#form"/>
</h:commandButton>
</div>
</h:panelGroup>
</h:form>
</ui:define>
model
#Named(value = "cursoModelo")
#SessionScoped
public class CursoModelo implements Serializable {
private String nombre;
//getters and setters...
}
controller 1
#Named(value = "cursoControlador")
#SessionScoped
public class CursoControlador implements Serializable{
#Inject private CursoModelo cursoModelo;
#Inject private Mensaje mensaje;
/**
* Creates a new instance of CursoControlador
*/
public CursoControlador() {
}
public String registrarCurso(){
//SOME CODE that inserts into de database
mensaje.mostrar(); //this is the problem probably i'm doing it wrong
return "index";
}
}
controller 2 (The controller for the message)
#Named(value = "mensaje")
#SessionScoped
public class Mensaje implements Serializable {
private String texto;
private boolean mostrarMensaje;
/**
* Creates a new instance of Mensaje
*/
public Mensaje() {
texto = "Mensaje a mostrar";
mostrarMensaje = false;
}
public void mostrar(){
mostrarMensaje = true;
}
public void esconder(){
mostrarMensaje = false;
}
//getters and setters from texto and mostrarMensaje
}
The reason why the topic has "kind of" is because I have an example in JSF 2.2 where I use a commandButton and call a bean function twice (depending on the url). It's basically the same code, which executes only in one example.
Here's the code with the description of the "error" below the code:
User bean
#ManagedBean
#RequestScoped
public class User {
private String name;
private String surname;
private int age;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public User(String name, String surname, int age, int id) {
super();
this.name = name;
this.surname = surname;
this.age = age;
this.id = id;
}
public User(){}
}
UsersBean bean:
#ManagedBean
#SessionScoped
public class UsersBean {
private List<User> listOfUsers = new ArrayList<User>();
private String passedParameter;
public UsersBean() {
listOfUsers.add(new User("Tywin", "Lannister", 60, 1));
listOfUsers.add(new User("Tyrion", "Lannister", 30, 2));
listOfUsers.add(new User("Jaime", "Lannister", 31, 3));
listOfUsers.add(new User("Cercei", "Lannister", 29, 4));
listOfUsers.add(new User("John", "Snow", 31, 5));
}
public List<User> getAll() {
System.out.println("getAall is called.");
return listOfUsers;
}
public User getDetails() {
passedParameter = (String) FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap().get("userID");
int id = Integer.parseInt(passedParameter);
User selected = null;
for (User u : listOfUsers) {
if (u.getId() == id) {
selected = u;
}
}
return selected;
}
public String addUser(User u) {
System.out.println("addUser is called.");
if (u.getId() != 0) {
for (User edit : listOfUsers) {
if (edit.getId() == u.getId()) {
System.out.println("Found it!");
edit.setAge(u.getAge());
edit.setName(u.getName());
edit.setSurname(u.getSurname());
}
}
} else {
u.setId(listOfUsers.size() + 1);
listOfUsers.add(u);
}
return "";
}
}
users.xhtml:
<f:view>
<!-- http://stackoverflow.com/questions/8083469/method-must-have-signature-string-method-etc-but-has-signature-void -->
<h:dataTable value="#{usersBean.all}" var="u">
<h:column>
<f:facet name="header">
User ID
</f:facet>
#{u.id}
</h:column>
<h:column>
<f:facet name="header">
Name
</f:facet>
#{u.name}
</h:column>
<h:column>
<f:facet name="header">
Details
</f:facet>
<h:link outcome="users" value="edit user">
<f:param name="userID" value="#{u.id}"></f:param>
<f:param name="action" value="edit"></f:param>
</h:link>
<h:link outcome="usersDetails" value="get details">
<f:param name="userID" value="#{u.id}"></f:param>
</h:link>
</h:column>
</h:dataTable>
<h:panelGroup rendered="#{param['action'] == 'edit'}">
<h1>Edit!</h1>
<h:form>
<ui:param name="editUser" value="#{usersBean.details}"></ui:param>
<h:outputText value="Name"></h:outputText>
<h:inputText value="#{editUser.name}"></h:inputText> <br />
<h:outputText value="Surname"></h:outputText>
<h:inputText value="#{editUser.surname}"></h:inputText> <br />
<h:outputText value="Age"></h:outputText>
<h:inputText value="#{editUser.age}"></h:inputText> <br />
<h:commandButton action="#{usersBean.addUser(editUser)}" value="Edit" type="submit"> </h:commandButton>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{empty param['action']}">
<h1>Add!</h1>
<h:form>
<h:outputText value="Name"></h:outputText>
<h:inputText value="#{user.name}"></h:inputText>
<h:outputText value="Surname"></h:outputText>
<h:inputText value="#{user.surname}"></h:inputText>
<h:outputText value="Age"></h:outputText>
<h:inputText value="#{user.age}"></h:inputText>
<h:commandButton action="#{usersBean.addUser(user)}" value="Add" type="submit"></h:commandButton>
</h:form>
</h:panelGroup>
</f:view>
OK, so, everything works perfectly. usersBean.addUser adds the user. If I add another inputText for ID and I put in an existing ID, the corresponding function updates the values. So, addUser function works as expected.
The problem is in case of
<h:panelGroup rendered="#{param['action'] == 'edit'}">
as you can see in the xhtml above, the code is basically the same, with the sole exception that I fill in the data of the user that was selected. This works, I get the appropriate data into input fields, but when I change them and click Edit, nothing happens. The function is not called! whereas in case of add, the function is called and it works.
It appears as if there is no action defined in case of edit, it only reloads the page (submit) without the actual action addUser.
How is this caused and how can I solve it?
It's because #{param['action'] == 'edit'} didn't evaluate true during processing the form submit and therefore the <h:panelGroup> is basically not rendered during processing the form submit, including the <h:commandButton> sitting in there. The form submit namely did not pass that parameter back to JSF. This way JSF won't see the <h:commandButton> and therefore never be able to decode and queue its action event. You need to make sure that all your rendered conditions evaluate the same during processing the form submit as they were during displaying the form (this is part of JSF's safeguard against tampered/hacked requests wherein the enduser would otherwise be able to execute unrendered command buttons like admin-only buttons).
So, you basically need to retain that parameter during the postbacks as well so that the rendered expression still evaluates true during processing the form submit. This can be achieved in several ways:
Add it as <f:param> inside the <h:commandButton> of relevance:
<h:commandButton action="#{usersBean.addUser(editUser)}" value="Edit" type="submit">
<f:param name="action" value="#{param.action}" />
</h:commandButton>
(note: param['action'] and param.action are equivalent; those brackets are only useful if the 'action' contains periods, or represents another variable)
Set it as a bean property by <f:viewParam>. Requirement is that the bean needs to be in view scope or broader. You've it in the session scope already, which is okay (but which is IMO way too broad, but that aside):
<f:metadata>
<f:viewParam name="action" value="#{usersBean.action}" />
</f:metadata>
...
<h:panelGroup rendered="#{usersBean.action}">
...
</h:panelGroup>
If you're already using JSF utility library OmniFaces, use its <o:form> instead of <h:form> which enables you submit to the current request URI instead of to the current JSF view ID. This way the GET request string will automagically be retained and you don't need to copypaste <f:param> over all command buttons like as in #1:
<o:form useRequestURI="true">
...
</o:form>
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated - point 6
I am learning JavaEE and I am playing around with ajax call with jsf and managedbean. I am trying to display the text as soon as i change the value from the drop down list. I see many people have the same problem on stackoverflow and i try to follow answers that are marked as accepted but I still can't make it work. Can someone please tell me where my error is?
Here is the jsf code:
<h:form>
<h:selectOneMenu id="productlist" value="#{productRepoMB.selectedProduct}">
<f:selectItems value="#{productRepoMB.productList}" var="product" itemValue="#{product.productID}" />
<f:ajax event="valueChange"
render="result"
listener="#{productRepoMB.selectMenuListener}" />
</h:selectOneMenu>
text changed: <h:outputText id="result" value="#{productRepoMB.text}" />
</h:form>
Here is managed bean code, Products list is populated from a database:
#Named
#RequestScoped
public class ProductRepoMB implements Serializable {
#EJB
private ProductsRepo productRepo;
private Products selectedProduct;
private List<Products> productList;
private String text="init text"; //use for testing ajax call
public ProductRepoMB() {
}
public Products getSelectedProduct() {
return selectedProduct;
}
public List<Products> getProductList() {
productList = productRepo.findAll();
return productList;
}
//The following code are used to testing ajax only!
public void selectMenuListener(AjaxBehaviorEvent e) {
setText("changed!");
}
public String getText() {
return text;
}
public void setText(String text){
this.text = text;
}
}
As you can see in the screenshot, I am having a JSF implicit value validation error for the property "member.city".
The purpose is to -through ajax - populate the city select One Menu after selecting a country on the previous Country Select one menu.
This is achieved by using selectOneMenu change listener and ajax to update the component displaying list of cities. However, I get a validation error and the form is not submitted.
I tried with Entities with and without (hascode and equals methods), but in vain.
I used a separated Managed Bean ViewScoped to retrive the filtered list, but in vain.
**
If I replace the filtered list of cities by the list that retrieves all
cities (in the xhtml form, replace <f:selectItems value="#{cityBean.citiesByCountry}" /> by <f:selectItems value="#{cityBean.all}" />, there's no validation error and the form successfully gets
submitted.
**
Here is the code:
Form:
<p:outputLabel for="country" value="#{i18n.country}" />
<p:selectOneMenu valueChangeListener="#{cityBean.selectCountry}" rendered="true" id="country" value="#{newMember.country}" converter="#{countryBean.converter}"
style="width:160px;text-align:left;border:thin solid gray;"
required="true" requiredMessage="#{i18n.required}">
<f:selectItems value="#{countryBean.all}" var="countryname" itemLabel="#{countryname.name}"/>
<p:ajax event="change" update="city" />
</p:selectOneMenu>
<p:message for="country" />
<p:outputLabel for="city" value="#{i18n.city}" />
<p:selectOneMenu rendered="true" id="city" value="#{newMember.city}" converter="#{cityBean.converter}" converterMessage="Conversion error!"
style="width:160px;text-align:left;border:thin solid gray;"
required="true" requiredMessage="#{i18n.required}">
<f:selectItems value="#{cityBean.citiesByCountry}" var="cityname" itemLabel="#{cityname.cityname}"/>
</p:selectOneMenu>
<p:message for="city" />
City Entity head:
#XmlRootElement
#Entity
#Table(name = "city", schema = "public")
#NamedQueries({
#NamedQuery(name = "City.findAll", query = "SELECT c FROM City c"),
#NamedQuery(name = "City.findById", query = "SELECT c FROM City c WHERE c.id = :id"),
#NamedQuery(name = "City.findByCountry", query = "SELECT c FROM City c WHERE c.country = :country"),
#NamedQuery(name = "City.findByCountryOrderedByName", query = "SELECT c FROM City c WHERE c.country = :country ORDER BY c.cityname ASC") })
#NamedNativeQueries({ #NamedNativeQuery(name = "City.findCountryNativeSQL", query = "select * from city c where c.countrybeta = :country.betacode", resultClass = City.class) })
public class City implements java.io.Serializable {
private static final long serialVersionUID = 3364735938266980295L;
private int id;
private Integer version;
private Country country;
private String cityname;
private String district;
private int population;
private Set<Member> members = new HashSet<Member>(0);
public City() {
}
// ...remainder of entity code (contructors and fields..)
The Bean:
#Named
#Stateful
#ConversationScoped
public class CityBean implements Serializable {
private static final long serialVersionUID = 1L;
private City city;
private List<City> cityList;
public City getCity() {
return this.city;
}
private Country selectedCountry;
#Inject
private Conversation conversation;
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
// remainder of code....
// The List that works fine with Select one Menu
public List<City> getAll() {
CriteriaQuery<City> criteria = this.entityManager.getCriteriaBuilder()
.createQuery(City.class);
return this.entityManager.createQuery(
criteria.select(criteria.from(City.class))).getResultList();
}
#PostConstruct
public void init() {
setCityList(getCitiesByCountry());
}
public List<City> getCityList() {
return cityList;
}
public void setCityList(List<City> cityList) {
this.cityList = cityList;
}
/**
* The List That triggers a jsf value validation error
* #return List of Cities by Country ordered by city name
*/
public List<City> getCitiesByCountry() {
TypedQuery<City> query = this.entityManager.createNamedQuery(
"City.findByCountryOrderedByName", City.class);
query.setParameter("country", selectedCountry);
return query.getResultList();
}
// The Select One Menu Listener
public void selectCountry(ValueChangeEvent event) {
if (event != null) {
selectedCountry = (Country) event.getNewValue();
}
}
// getters and setters....
ENV:
- JSF 21
- Hibernate
- JBoss AS 7.1
- PrimeFaces 3.5
- Linux 3.5 / Firefox 18.0.2
Your concrete problem is caused because you aren't starting the conversation in #PostConstruct and thus the bean behaves like request scoped. This way the list of cities will incompatibly change during processing the form submit and become empty (and thus the selected item couldn't be found in the list of available items and hence this validation error).
You need to start the conversation in the #PostConstruct.
conversation.begin();
See also:
How to replace #ManagedBean / #ViewScope by CDI in JSF 2.0/2.1
Validation Error: Value is not valid
Unrelated to the concrete problem, using valueChangeListener and performing business logic in getter methods is the wrong approach. You should not be performing business logic in getter methods at all. This is plain inefficient. They should solely return already-prepared bean properties.
Rewrite your logic as follows:
<p:selectOneMenu value="#{bean.country}" ...>
<f:selectItems value="#{bean.countries}" />
<p:ajax listener="#{bean.updateCities}" update="city" />
</p:selectOneMenu>
<p:selectOneMenu id="city" value="#{bean.city}" ...>
<f:selectItems value="#{bean.cities}" />
</p:selectOneMenu>
with
private Country country;
private List<Country> countries;
private City city;
private List<City> cities;
#PostConstruct
public void init() {
countries = service.listCountries();
public void updateCities() {
cities = service.listCities(country);
}
// Add/generate normal!! getters/setters.
See also:
Why JSF calls getters multiple times
Our selectOneMenu tag wiki page
I have a page with a "rich:DataTable". For each table row, added a checkbox to allow the user to select more than one line before excute an operation. How to retrieve the rows that were selected?
Just add a boolean , maybe called selected , to the POJO that represents a row for the rich:DataTable . And bind this boolean to the <h:selectBooleanCheckbox> in the <rich:column> inside the <rich:dataTable>
For example , your beans , POJO and the view may look likes this:
<rich:dataTable value="#{myBean.customerList}" var="customer">
<rich:column>
<h:selectBooleanCheckbox value="#{customer.selected}" />
</rich:column>
<rich:column>
<h:outputText value="#{customer.name}" />
</rich:column>
<rich:column>
<h:outputText value="#{customer.address}" />
</rich:column>
</rich:dataTable>
public class MyBean {
private List<Customer> customerList;
//getter and setter for the customerList
}
public class Customer{
private boolean selected;
private String name;
private String address;
//getter and setter for the properties
}
To retrieve the rows that were selected , just iterate the MyBean.customerList and check if the selected property of the Customer is true.
[SOLVED] I decided otherwise. Added a "Map " and a column with "CheckBox" this way: "". By submitting the form, the lines of "Map" that are selected will get the Boolean property equal "true".
My Bean:
public class Bean<T extends Object> {
private Map<T, Boolean> selectedRowsMap = new HashMap<T, Boolean>(0);
...
public Set<T> getSelectedRows() {
selectedRows.clear();
for (T key : getSelectedRowsMap().keySet()) {
if (getSelectedRowsMap().get(key) == true){
selectedRows.add(key);
}
}
return selectedRows;
}
}
My XHTML:
<rich:dataTable>
<rich:column>
<h:selectBooleanCheckbox value="#{bean.selectedRowsMap[row]}" />
</rich:column>
<rich:column>
<h:outputText value="${row.age}" />
</rich:column>
...
<rich:dataTable>