Selected element not set to the backing bean - jsf

I have following field in my jsp. Selected value is not set to the element.
When I did Inspect element on this drop down field in FF, it shows that element is not selected. Also its not set to the backing bean.
What am I missing?
<h:selectOnMenu id="scriptEngine" value="#{AddScriptBean.scriptEngine}" required="true">
<f:selectItems value="#{AddScriptBean.scriptEngines}"/>
</h:selectOneMenu>
The backing bean code is as follows
public List<SelectItem> getScriptEngines() {
List<SelectItem> items = new ArrayList<SelectItem>();
try {
GetScriptEngineNamesCommand command = (GetScriptEngineNamesCommand) CommandFactory.getInstance().getCommand(GetScriptEngineNamesCommand.class.getName());
command.doExecute();
Map<String, String> engineNames = command.getEngineNames();
MessageSource messageSource = getMessageSource();
Locale locale = RequestUtils.getUserLocale((HttpServletRequest) FacesContext.getCurrentInstance()
.getExternalContext().getRequest(), Globals.LOCALE_KEY);
String label = messageSource.getFormattedMessage(locale, "com.soa.console.faces.script.select", new Object[] {});
items.add(new SelectItem("", label));
for (String name : engineNames.keySet()){
items.add(new SelectItem(engineNames.get(name), name));
}
}catch (GException e){
String eMessage = e.toString();
FacesMessage msg = new FacesMessage("", eMessage);
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
return items;
}

I guess getter method is not calling becuase you have bounded <f:selectItems> tag to the bean with scriptEngines but your getter method is getScriptEngines().It should have been getscriptEngines().This could be the problem

Related

Dynamically preselect items in HtmlSelectOneMenu with OmniFaces SelectItemsConverter

I want to dynamically create a dropdown (HtmlSelectOneMenu) with all possible options. The option currently set should be preselected. To achieve this I created a value expression for my form:
String jsfValue = String.format("#{%s.item.%s}", getControllerBeanName(), key);
ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, String.class);
menu.setValueExpression("value", valueExpression);
The string #{%s.item.%s} evaluates to #{playlistController.item.category} and category is the object of type Category that I want to bind to my dropdown.
To use the SelectItemsConverter from OmniFaces I changed the toString() method of Category to:
#Override
public String toString() {
return String.format("%s[id=%d]", getClass().getSimpleName(), getId());
}
My code for the form generation looks like this (Note: Category extends BaseEntity):
private UIComponent createDropdown(FormInput property) {
String key = property.getKey();
String beanName = key + "Controller";
GenFormBaseController controller = (GenFormBaseController) JSFUtils.getManagedBean(beanName);
List<BaseEntity> list = controller.getService().findAll();
List<SelectItem> selectItems = new ArrayList<>(list.size());
for (BaseEntity itemInList : list) {
selectItems.add(new SelectItem(itemInList, itemInList.getName()));
}
UISelectItems items = new UISelectItems();
items.setValue(selectItems.toArray());
HtmlSelectOneMenu menu = new HtmlSelectOneMenu();
menu.setConverter(new SelectItemsConverter());
menu.setId(key);
menu.getChildren().add(items);
String jsfValue = String.format("#{%s.item.%s}", getControllerBeanName(), key);
ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, String.class);
menu.setValueExpression("value", valueExpression);
return menu;
}
If I set menu.setConverter(new SelectItemsConverter()); then the wrong item is preselected. But If I remove it, then the correct item is selected but when I try to save the form it fails because it has no converter for the dropdown.
Can anyone help me? I've published the code on GitHub.
The method createDropdown can be found at the bottom of the linked code.
Your mistake is here, in the String.class type argument:
ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, String.class);
A Category is not a String. Use Category.class instead and everything should be well. At least, theoretically. I can't copy'n'paste'n'run this piece of code without changes.
I found the error in the code. Simply the equals() and hashCode() of the BaseEntity were insufficient. And the error BalusC already mentioned. The resulting code looks like the following:
GenFormBaseController.java
private UIComponent createDropdown(FormInput property) {
String key = property.getKey();
Class<?> expectedType = property.getValue();
String beanName = lowerFirstChar(expectedType.getSimpleName()) + "Controller";
GenFormBaseController controller = (GenFormBaseController) JSFUtils.getManagedBean(beanName);
List<BaseEntity> list = controller.getService().findAll();
List<SelectItem> selectItems = new ArrayList<>(list.size());
for (BaseEntity itemInList : list) {
selectItems.add(new SelectItem(itemInList, itemInList.getName()));
}
UISelectItems items = new UISelectItems();
items.setValue(selectItems);
HtmlSelectOneMenu menu = new HtmlSelectOneMenu();
menu.setConverter(new SelectItemsConverter());
menu.setId(key);
menu.getChildren().add(items);
String jsfValue = String.format("#{%s.item.%s}", getControllerBeanName(), key);
ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, expectedType);
menu.setValueExpression("value", valueExpression);
return menu;
}
BaseEntity.java
#Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.id);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final BaseEntity other = (BaseEntity) obj;
if (!Objects.equals(this.id, other.id)) {
return false;
}
return true;
}

Populating selectItems of the combobox (label, value) using a managed bean

I have a combo in my page which I want to have populated with some keywords from configuration. I want to use a managed bean to accomplish it.
Let's say that I have a bean called Config, where there is a List categories field. ..
public class Configuration implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> categories;
public List<String> getCategories() {
if (categories == null)
categories = getCats();
return categories;
}
//... etc.
}
When I use this field for my combo, it works well...
<xp:comboBox>
<xp:selectItems>
<xp:this.value><![CDATA[#{config.categories}]]></xp:this.value>
</xp:selectItems>
</xp:comboBox>
But, it's only a list of labels. I need values, too. How do I populate selectItems of my combo with TWO strings - a label and a value?
EDIT:
I tried to create an object Combo with label and value fields and use a repeat inside my comboBox.
<xp:comboBox>
<xp:repeat id="repeat1" value="#{config.combo}" var="c" rows="30">
<xp:selectItem itemLabel="#{c.label}" itemValue="#{c.value}" />
</xp:repeat>
</xp:comboBox>
Still not working... :-(
Instead of returning a List<String> your function should return a List<javax.faces.model.SelectItem>. Here's a sample:
public static List<SelectItem> getComboboxOptions() {
List<SelectItem> options = new ArrayList<SelectItem>();
SelectItem option = new SelectItem();
option.setLabel("Here's a label");
option.setValue("Here's a value");
options.add(option);
return options;
}
Advantage of using this method (besides not having to use that nonconceptual stuff :-) is that you can also the SelectItemGroup class to group the options:
public static List<SelectItem> getGroupedComboboxOptions() {
List<SelectItem> groupedOptions = new ArrayList<SelectItem>();
SelectItemGroup group = new SelectItemGroup("A group of options");
SelectItem[] options = new SelectItem[2];
options[0] = new SelectItem("here's a value", "here's a label");
options[1] = new SelectItem("here's a value", "here's a label");
group.setSelectItems(options);
groupedOptions.add(group);
return groupedOptions;
}
You can use SelectItems. (see http://docs.oracle.com/javaee/6/api/javax/faces/model/SelectItem.html)
You can specify both value and label, or value only.
import javax.faces.model.SelectItem;
public List<SelectItem> getCategories() {
try {
ArrayList<SelectItem> ret = new ArrayList<SelectItem>();
ret.add(new SelectItem("my value", "my label"));
return ret;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

rich:pickList & selectManyListbox - JSF Converter Validation Error: Value not valid [duplicate]

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

Passing parameter to completeMethod of p:autoComplete

I'm using the PrimeFaces p:autoComplete widget in a search form of my project. The user can choose how many and which form-elements (search parameters) he wants to include so I need to pass an ID to the completeMethod for each of them. I've tried adding onfocus=".." to pass the object to the bean but that only would be activated when the element first is loaded.
My question: How can I pass an attribute to the completeMethod?
XHTML of the element (simple):
<p:autoComplete value="#{filter.value}" label="dynamic search attribute"
completeMethod="#{myBean.complete}" />
The bean (simple):
#Named("myBean")
public class MyController implements Serializable {
public List<String> complete(String query) {
List<String> results = new ArrayList<String>();
// ... code
return results;
}
}
In theory this would seem like the perfect solution:
<p:autoComplete value="#{filter.value}" label="dynamic search attribute"
completeMethod="#{myBean.complete(filter)}" />
And again the bean:
#Named("myBean")
public class MyController implements Serializable {
public List<String> complete(String query, FilterObject o) {
List<String> results = new ArrayList<String>();
// ... database query based on FilterObject o
return results;
}
}
You can set it as an attribute:
<p:autoComplete value="#{filter.value}" label="dynamic search attribute" completeMethod="#{myBean.complete}">
<f:attribute name="filter" value="#{filter}" />
</p:autoComplete>
and get it by UIComponent#getCurrentComponent():
public List<String> complete(String query) {
FacesContext context = FacesContext.getCurrentInstance();
FilterObject o = (FilterObject) UIComponent.getCurrentComponent(context).getAttributes().get("filter");
// ...
}
Alternatively, as that #{filter} appears in your case to be already in the EL scope, you can also leave the <f:attribute> away and get it by evaluating the EL expression programmatically with help of Application#evaluateExpressionGet():
public List<String> complete(String query) {
FacesContext context = FacesContext.getCurrentInstance();
FilterObject o = context.getApplication().evaluateExpressionGet(context, "#{filter}", FilterObject.class);
// ...
}
Or, if it is also a #Named bean, then you can just #Inject it in the parent bean:
#Inject
private FilterObject o;

How to fill a selectManyListbox from a database

I would like to know how I can fill a h:selectManyListbox from a database, i.e. not with static options.
Use <f:selectItems> in combination with a property which returns List<SelectItem>, or when you're already on JSF 2.0, a List<SomeObject>.
<h:selectManyListbox value="#{bean.selectedItems}">
<f:selectItems value="#{bean.selectItems}" />
</h:selectManyListbox>
You can load the items from the DB in bean's constructor or #PostConstruct method.
public class Bean {
private List<String> selectedItems;
private List<SelectItem> selectItems;
public Bean() {
selectItems = new ArrayList<SelectItem>();
// Fill select items during Bean initialization/construction.
// Below is just an example, you could replace this by getting a list
// of some objects from DB and creating new items in a loop.
selectItems.add(new SelectItem("value1", "label1"));
selectItems.add(new SelectItem("value2", "label2"));
selectItems.add(new SelectItem("value3", "label3"));
}
// Getters, etc
}

Resources