I have tried to set up two selectOneMenus where the first selects an object, and in the second, one of five object parameters can be selected. For this, I have built a custom converter, and "id" should be passed from the first selectOneMenu to uniqely identify the object for the second menu. On page load, the getAsString method is called and iterated through all items of the first menu. On selecting an item, getAsObject is called, and the correct id is used and the correct Question is returned. But: the second menu is not populated at all. What did I miss? Help appreciated, I'm still learning this.
Here is the first selectOneMenu:
<h:selectOneMenu converter="answerDeliverer">
<f:selectItems value="#{questionBean.list}" var="question" itemValue="#{question}" itemLabel="#{question.text}"/>
<f:ajax event="change" render="answers"></f:ajax>
</h:selectOneMenu>
And the second:
<h:selectOneMenu id="answers">
<f:selectItem itemValue="1" itemLabel="#{question.a1}"/>
<f:selectItem itemValue="2" itemLabel="#{question.a2}"/>
<f:selectItem itemValue="3" itemLabel="#{question.a3}"/>
<f:selectItem itemValue="4" itemLabel="#{question.a4}"/>
<f:selectItem itemValue="5" itemLabel="#{question.a5}"/>
</h:selectOneMenu>
And the converter:
#ManagedBean (name = "answerDeliverer")
#RequestScoped
public class answerDeliverer implements Converter {
#EJB
private QuestionDAO questionDAO;
#Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
return questionDAO.get(Integer.parseInt(s));
}
#Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) {
if (o instanceof Question) {
return ((Question) o).getId();
} else if (o instanceof String) {
return ((String) o);
} else throw new ConverterException(new FacesMessage(o + " is not a valid Question or id"));
//just so I can try either question or question.id as itemValues
}
}
The error log is empty, only on startup I get several warnings of this type:
Unknow type constant pool 18 at position 7
Nov 03, 2015 1:17:10 PM com.sun.faces.config.JavaClassScanningAnnotationScanner$ConstantPoolInfo containsAnnotation
After hours and hours of debugging, I'm stuck now. I can conclude the following:
The answerDeliverer seems to work. On selection, getAsObject fetches the correct question from the DB
XHR is registered in browser debug mode on every selection with a "200 OK" server answer
There is no other error in the log
I seem to have something wrong with bringing the question into the scope of the second selectOneMenu. Question is not a ManagedBean, and in my QuestionBean, private Question question = new Question(), so, could it be that no matter what I do in the first selectOneMenu, the second always generates a new instance and doesn't care about what I did before? And thus erases any question I had before? How could I avoid that?
I'm using Mojarra-2.2.1 on Tomcat 8. Also tried to go with OmniFaces, but got stuck in an infinite loop of debugging the setup there before giving up on that.
I'll add my Question POJO and the Bean if that helps any.
public class Question implements Serializable {
private String text;
private String a1 = Constants.defaulta1;
private String a2 = Constants.defaulta2;
private String a3 = Constants.defaulta3;
private String a4 = Constants.defaulta4;
private String a5 = Constants.defaulta5;
private String id;
private String nodeid;
//getters and setters
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((Question) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
#Override
public String toString() {
return id;
}
}
and the QuestionBean:
#ManagedBean (name="questionBean")
#ViewScoped
public class QuestionBean implements Serializable {
private List<Question> list;
//this was for a CRUD page; I deleted the other irrelevant code
private Question question = new Question();
private boolean edited;
#EJB
private QuestionDAO questionDAO;
#PostConstruct
public void init() {
list = questionDAO.getAll();
}
public List<Question> getList() {
return list;
}
public Question getQuestion() {
return question;
}
}
Related
Some help would be appreciated on this issue.
I have several JSF Pages, but one of them is a Subcategory where there is a SelectOneMenu for choosing the Category, but when I try to edit the subcategory, this SelectOneMenu always shows the first value and is not getting preselected.
How could I solve this. I have read a lot of SO posts and eventhougn I have implemented a couple of advice I have not achieved it. for example, a #BalusC's one:
primefaces selectOneMenu doesn't working when it should
Here is the View
<h:outputText value="Subcategory ID : #{subcategoryController.subcategory.subcategoryId}"/>
<p:selectOneMenu id="cboCategoryDialog" converter="subcategoryConverter"
value="#{subcategoryController.category.categoryId}"
style="width: 100%"
<f:selectItems value="#{subcategoryController.categoryList}"
var="subcat"
itemLabel="#{subcat.categoryName}"
itemValue="#{subcat.categoryId}"/>
</p:selectOneMenu>**
This is the Subcategory Entity:
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "subcategory_id")
private Integer subcategoryId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "subcategory_name")
private String subcategoryName;
#JoinColumn(name = "category_id", referencedColumnName = "category_id")
#OneToOne(optional = false;
private Category categoryId;
//Getters and Setters Methods
#Override
public int hashCode() {
return (subcategoryId != null)
? (this.getClass().hashCode() + subcategoryId.hashCode())
: super.hashCode();
}
#Override
public boolean equals(Object obj) {
return (obj instanceof Subcategory) && (subcategoryId != null)
? subcategoryId.equals(((Subcategory) obj).subcategoryId)
: (obj == this);
}
SubcategoryConverter
#RequestScoped
#FacesConverter("subcategoryConverter")
public class SubcategoryConverter implements Converter {
#EJB
private SubcategoryFacadeLocal EJBsubcategory;
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Subcategory) || ((Subcategory) value).getSubcategoryId() == null) {
return null;
}
return String.valueOf(((Subcategory) value).getSubcategoryId());
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || !value.matches("\\d+")) {
return null;
}
Subcategory subcategory = EJBsubcategory.find(Integer.valueOf(value));
if (subcategory == null) {
throw new ConverterException(new FacesMessage("Unknown operation ID: " + value));
}
return subcategory;
}
}
Below image shows the Dialog when I try to edit any item, it always shows computer because its the first itm.
See the image showing the issue
I have been reading some related useful questions about this, but I have not been able to fix it.
Thanks in advance,
After reviewing the code, I realized that I was using the wrong entity in the JSF.:
For example:
In the question, I posted: subcategoryController.category.categoryId to save Category object in a subcategory object.
As follow:
<p:selectOneMenu id="cboCategoryDialog" converter="subcategoryConverter"
value="#{subcategoryController.category.categoryId}"
But, the correct approach is the following:
<p:selectOneMenu id="cboCategoryDialog" converter="omnifaces.SelectItemsConverter"
value="#{subcategoryController.subcategory.categoryId}"
Now, this component is "pointing" to the correct object.
Another issue that I was facing is:
As I have nested entities, example: Entity.entity.entity they should be inicialized in every level, before using it.
Some posts that really, really, really helped me on this:
On solving the nested entities issues (PropertyNotFoundException).
Identifying and solving javax.el.PropertyNotFoundException: Target Unreachable
On using normal and OmniFaces Converter on SelectOneMenu:
http://balusc.omnifaces.org/2007/09/objects-in-hselectonemenu.html
Be sure that your entity hashCode() and equals() methods are implemented and doesn't try to access nullable fields. That worked for me!
I have a <p:selectCheckboxMenu> and I want to get the selected values back in bean. But the value I receive
when I select an item from the menu it's a string, representing the type field from the CategorizationBean.
I just want when I select an item from the table, to get the whole CategorizationBean structure in the bean.
This is the snippet from the xhtml page:
<p:selectCheckboxMenu label="Categorization"
value="#alertMB.selectedCategories}"
converter="com.converter.CategoryConverter">
<f:selectItems value="#{alertMB.categoryDomainEntry}"
var="category"
itemLabel="#{category.type}"
itemValue="#{category}"/>
</p:selectCheckboxMenu>
Snippet from bean:
public List<CategorizationBean> getSelectedCategories() {
return selectedCategories;
}
public void setSelectedCategories(List<CategorizationBean> selectedCategories) {
this.selectedCategories = selectedCategories;
}
public class CategorizationBean implements Serializable{
private String type;
private long id;
I think that you have missed by using a list of beans, I use this example and it works:
<p:selectCheckboxMenu id="slctRdBtn"
value="#{yourBean.compLovDtgrid}"
converter="compLovDtgridConverter">
<f:selectItems
value="#{yourBean.listCompLovDtgrid}"
var="rdbtn" itemLabel="#{rdbtn.vjlrLibelleRep}"
itemValue="#{rdbtn}" />
</p:selectCheckboxMenu>
and for the converter:
#FacesConverter(forClass=CompLovDtgrid.class , value="compLovDtgridConverter")
public class CompLovDtgridConverter implements Converter{
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
return (value instanceof CompLovDtgrid) ? ((CompLovDtgrid) value).getVjlrCodeRep() : null;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,String value)
{
if(value == null)
return null;
YourBean data = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
for(CompLovDtgrid compLovDtgrid : data.getListCompLovDtgrid())
{
if(compLovDtgrid.getVjlrCodeRep().equals(value))
return compLovDtgrid;
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to CompLovDtgrid", value)));
}
}
and for the list, I use:
public List<CompLovDtgrid> getListCompLovDtgrid()
{
return listCompLovDtgrid;
}
public void setListCompLovDtgrid(List<CompLovDtgrid> listCompLovDtgrid) {
this.listCompLovDtgrid = listCompLovDtgrid;
}
I tried to make same list as in the below link:
http://www.primefaces.org/showcase/ui/input/listbox.xhtml
Unfortunately I get validation error:
Select Box: Validation Error: Value is not valid
I don't know where I've made mistake. I read a lot about this issue (in most cases it was solved by BalusC) however still can't find the issue.
If you can help I really appreciate.
StoreHouse.java
#Entity
public class StoreHouse implements Serializable {
#Id
#GeneratedValue
private Integer id;
#OneToOne
private Supply supply;
#Column(nullable = false)
private Integer amount;
//geters setters namedqueryies
Supply.java
#Entity
public class Supply implements Serializable {
#Id
#GeneratedValue
private Integer id;
#Column(unique = true, nullable = false, length = 32)
private String name;
#Column(length = 1024)
private String description;
#Column
private Double price;
#Enumerated(EnumType.STRING)
#NotNull
private SupplyType supplyType;
//geters setters namedqueryies
StoreHouseController.java
#ManagedBean
public class StoreHouseController implements Serializable {
#Inject
private StoreHouseBean storeHouseBean; // DAO for storeHouse
#ManagedProperty("#{supplyController}")
private SupplyController supplyController; //Manged bean for Supply
private StoreHouse storeHouse = new StoreHouse();
private List<Supply> allSupplies;
#PostConstruct
public void init() {
allSupplies = supplyController.findAll();
}
public void check() {
System.out.println("storeHousetheme" + storeHouse.toString()); // Function just to check if the supply was set
}
SupplyConverter.java
#FacesConverter("supplyConverter")
public class SupplyConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && value.trim().length() > 0) {
try {
SupplyController supplyController = (SupplyController) context.getExternalContext().getApplicationMap().get("supplyController");
Supply supply = supplyController.findById(Integer.parseInt(value));
System.out.println("CONVERTER:" + supply.toString());
return supply;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion error", "ERROR."));
}
} else {
return null;
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
if (object != null) {
return String.valueOf(((Supply) object).getId());
} else {
return null;
}
}
}
view.xhtml
<h:form>
<p:messages autoUpdate="true" />
<h:panelGrid columns="2">
<h:outputLabel value="Nazwa" />
<p:selectOneListbox id="supplies" value="#{storeHouseController.storeHouse.supply}" converter="supplyConverter" var="s" filter="true" label="Select Box">
<f:selectItems value="#{storeHouseController.allSupplies}" var="supply" itemLabel="#{supply.name}" itemValue="#{supply}"/>
<p:column>
<h:outputText value="#{s.name}" />
</p:column>
</p:selectOneListbox>
<p:commandButton action="#{storeHouseController.check}" type="submit" value="submit" />
</h:panelGrid>
If you need any other file/class please just add comment.
Your SelectItem-value is your object, so the toString()-method is called and you get something like "Supply#44de6bae".
Then, your controller tries to parse it to int and fails. Try...
<f:selectItems value="#{...}" var="..." itemLabel="..." itemValue="#{supply.id}"/>
If the error occures while loading the page, this might help as well as at least some components don't like "none-basic-type-or-enum" values. I hope this helped, Greez.
After few more days of searching and googling I've found the solution. I'm a bit angry that this is not posted anywhere on primefaces because I've lost many hours to fix the issue.
It came out that I did not overwrite equals method of Supply class which is required.
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Supply other = (Supply) obj;
if (!Objects.equals(this.id, other.id)) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
if (this.supplyType != other.supplyType) {
return false;
}
return true;
}
I believe that this topic will help someone in the future...
This question already has answers here:
Pick Custom Object from Select one menu JSF [duplicate]
(2 answers)
Closed 9 years ago.
i want to pick custom object from select one menu. it neither shows an error nor values. what to do? please help me. thanks in advance.
Now i'm seeing Null pointer exception at getAsObject at this line:
return getCurrencyService().getCurrencyBtId(currencyId);
this is my xhtml document
<h:panelGrid columns="2">
<p:outputLabel value="" />
<p:selectOneMenu id="CurrencyMenu" value="#{CurrencyMB.currency}" converter="currencyConverter" >
<f:selectItems value="#{CurrencyMB.currencyList}" var="currency" itemValue="#{currency}" itemLabel="#{currency.currencyName}" >
</f:selectItems>
<p:ajax update="currencyOut" />
</p:selectOneMenu>
<p:outputLabel value="Currency Id : #{CurrencyMB.currency.currencyId}" id="currencyOut" />
</h:panelGrid>
this is my managedBean class.
#ManagedBean(name = "CurrencyMB")
#RequestScoped
public class CurrencyManagedBean implements Serializable{
private Currency currency;
private List<Currency> currencyList;
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
public List<Currency> getCurrencyList() {
currencyList = new ArrayList<Currency>();
currencyList.addAll(getiCurrencyService().getCurrencies());
return currencyList;
}
public void setCurrencyList(List<Currency> currencyList) {
this.currencyList = currencyList;
}
}
this is my currencyConverter class code:
**#FacesConverter("currencyConverter")
public class CurrencyConverter implements Converter {
#ManagedProperty(value = "#{currencyService}")
private ICurrencyService currencyService;
public ICurrencyService getCurrencyService() {
return currencyService;
}
public void setCurrencyService(ICurrencyService currencyService) {
this.currencyService = currencyService;
}
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
int currencyId = Integer.valueOf(value);
return getCurrencyService().getCurrencyBtId(currencyId);
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
Currency currency = (Currency) value;
int currencyId = currency.getCurrencyId();
return String.valueOf(currencyId);
}
}**
As it can be seen in many questions, and their answers, here, #FacesConverter is not eligible for injection. In your case, you try to inject your service via #ManagedProperty, which yields NPE, as Luiggi told you in a comment to your initial question.
With the current JSF release, all you can do to inject services in your converters is to give up using #FacesConverter and switch to using standard #ManagedBean annotation, thus referring to your converter not by id, but by bean name, like in converter="#{currencyConverter}".
This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 7 years ago.
I replaced rich:pickList to selectManyListbox for testing, when submit values to server it still displays error: "Validation Error: Value is not valid". I also set breakpoint to debug StaffConverter (getAsobject), but system never invokes it. Pls let me know some reasons why my converter never invoked and suggest me how to fix this. thanks
xhtml file:
<h:selectManyListbox value="#{reportController.selectedStaffList}" converter="staffConverter">
<f:selectItems value="#{reportController.staffList}"
var="item" itemValue="#{item}" itemLabel="#{item.name}" />
</h:selectManyListbox>
<rich:pickList value="#{reportController.selectedStaffList}" converter="staffConverter" sourceCaption="Available flight" targetCaption="Selected flight" listWidth="195px" listHeight="100px" orderable="true">
<f:selectItems value="#{reportController.staffList}" var="item" itemValue="#{item}" itemLabel="#{item.name}" />
</rich:pickList>
My Converter:
#FacesConverter(forClass = Staff.class)
public static class StaffConverter implements Converter {
public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
if (value == null || value.length() == 0) {
return null;
}
StaffController controller = StaffController.getInstance();
return controller.facade.find(Staff.class, getKey(value));
}
java.lang.Integer getKey(String value) {
java.lang.Integer key;
key = Integer.valueOf(value);
return key;
}
String getStringKey(java.lang.Integer value) {
StringBuffer sb = new StringBuffer();
sb.append(value);
return sb.toString();
}
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Staff) {
Staff o = (Staff) object;
return getStringKey(o.getStaffCode());
} else {
throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName()
+ "; expected type: " + StaffController.class.getName());
}
}
}
I implemented equals method in Staff:
#Override
public boolean equals(Object object) {
if (!(object instanceof Staff)) {
return false;
}
Staff other = (Staff) object;
return (this.staffCode == other.staffCode);
}
Just to have it posted somewhere for people - like me - who like searching for solutions for development issues on google or stackoverflow...
I had this issue several times, depending on which kind of pojos I was using in the converters... Finally I think I found an elegant solution.
In my case I use JPA entity classes directly as I wanted to save the DTO layer. Well, with some entities the rich:pickList worked, with others not... I also tracked it down to the equals method. In the example below for instance it did not work for the userGroupConverter bean.
My solution is simply to inline overwrite the equals method, so the one from the entity (I often use lombok) is untouched and does not need to be changed, at all! So in my converter below I only compare the name field in equals:
xhtml:
<rich:pickList id="pickListUserGroupSelection"
value="#{usersBean.selectedUserGroups}" switchByDblClick="true"
sourceCaption="Available user groups" targetCaption="Groups assigned to user"
listWidth="365px" listHeight="100px" orderable="false"
converter="#{userGroupConverter}"
disabled="#{!rich:isUserInRole('USERS_MAINTAIN')}">
<f:validateRequired disabled="true" />
<rich:validator disabled="true" />
<f:selectItems value="#{usersBean.userGroups}" var="userGroup"
itemValue="#{userGroup}"
itemLabel="#{userGroup.name}" />
<f:selectItems value="#{usersBean.selectedUserGroups}" var="userGroup"
itemValue="#{userGroup}"
itemLabel="#{userGroup.name}" />
</rich:pickList>
<rich:message for="pickListUserGroupSelection" />
Converter:
package ...;
import ...
/**
* JSF UserGroup converter.<br>
* Description:<br>
* JSF UserGroup converter for rich:pickList elements.<br>
* <br>
* Copyright: Copyright (c) 2014<br>
*/
#Named
#Slf4j
#RequestScoped // must be request scoped, as it can change every time!
public class UserGroupConverter implements Converter, Serializable {
/**
*
*/
private static final long serialVersionUID = 9057357226886146751L;
#Getter
Map<String, UserGroup> groupMap;
#Inject
MessageUtil messageUtil;
#Inject
UserGroupDao userGroupDao;
#PostConstruct
public void postConstruct() {
groupMap = new HashMap<>();
List<UserGroup> userGroups;
try {
userGroups = userGroupDao.findAll(new String[] {UserGroup.FIELD_USER_ROLE_NAMES});
if(userGroups != null) {
for (UserGroup userGroup : userGroups) {
// 20150713: for some reason the UserGroup entity's equals method is not sufficient here and causes a JSF validation error
// "Validation Error: Value is not valid". I tried this overridden equals method and now it works fine :-)
#SuppressWarnings("serial")
UserGroup newGroup = new UserGroup() {
#Override
public boolean equals(Object obj){
if (!(obj instanceof UserGroup)){
return false;
}
return (getName() != null)
? getName().equals(((UserGroup) obj).getName())
: (obj == this);
}
};
newGroup.setName(userGroup.getName());
groupMap.put(newGroup.getName(), newGroup);
}
}
} catch (DaoException e) {
log.error(e.getMessage(), e);
FacesContext fc = FacesContext.getCurrentInstance();
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Error initializing user group converter!", null);
fc.addMessage(null, message);
}
}
/*
* (non-Javadoc)
* #see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
*/
#Override
public UserGroup getAsObject(FacesContext context, UIComponent component,
String value) {
UserGroup ug = null;
try {
ug = getGroupMap().get(value);
} catch (Exception e) {
log.error(e.getMessage(), e);
FacesContext fc = FacesContext.getCurrentInstance();
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Error converting user group!", null);
fc.addMessage(null, message);
}
return ug;
}
/*
* (non-Javadoc)
* #see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
*/
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
String name = ((UserGroup) value).getName();
return name;
}
}
Brgds and have fun!
Thanks Brian and BalusC for your help. I fixed my problem by adding named converter inside selectManyListbox & rich:picklist so they run well. But normally, I only use #FacesConverter(forClass = Staff.class), and don't need to add named converter in jsf so they still run well except for selectManyListbox & rich:picklist