Iam new to JSF technology, currently in our project we are using JSF 2.0 with spring and hibernate integration.I have one doubt regarding h:selectOneMenu and f:selectItems.
From the Database i'm getting a list of UserBeans.I'm using like
<h:selectOneMenu id="users" value="#{MyBean.user}">
<f:selectItems value="#{MyBean.userList}" var="user" itemLabel="#{user.userName}" itemValue="#{user.userId}" />
</h:selectOneMenu>
Here user is of type UserBean and userList is the list of UserBeans.
In the view page it is showing correctly but in the backing bean when i select one item and click on submit button, it showing the selected user as NULL.
My doubt is i can only pass List of SelectItem objects for the f:selectItems or any other beans list ..??
Is there any other way to populate the list of UserBeans otherthan SelectItem for selectItems.
Thank you All,
Anil
You have set the user ID as value of the select items, but you seem to be trying to bind the value to a fullworthy User property in the bean. You need to bind it as an user ID.
<h:selectOneMenu id="users" value="#{MyBean.userId}">
<f:selectItems value="#{MyBean.userList}" var="user" itemLabel="#{user.userName}" itemValue="#{user.userId}" />
</h:selectOneMenu>
If your sole intent is to be able to select and set User instead of only the ID, then you need a Converter to convert between User and String. That's because HTTP/HTML doesn't understand Java objects. It only understands strings. Here's a kickoff example:
<h:selectOneMenu id="users" value="#{MyBean.user}">
<f:selectItems value="#{MyBean.userList}" var="user" itemLabel="#{user.userName}" itemValue="#{user}" />
</h:selectOneMenu>
with
#FacesConverter(forClass=User.class)
public class UserConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
return userService.findById(Long.valueOf(value));
} catch (SomeException e) {
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to User", value)), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((User) value).getId());
}
}
However, this is a pretty expensive job. I'd suggest to stick to passing ID around and obtain the real user in the bean's action method just once instead of letting the converter do it for every single item.
Related
I have this select:
<p:selectOneMenu value="#{bean.val}">
<f:selectItem itemValue="X" itemLabel="Select" />
<f:selectItems value="#{bean.getVals()}" />
<p:ajax update="wrapper" />
</p:selectOneMenu>
And this is the wrapper it updates:
<p:panel id="wrapper">
<p:panel rendered="#{bean.val == 'A' or bean.val == 'B'}">
<!-- insert your code here -->
</p:panel>
</p:panel>
The wrapper is outside the select, at the same level.
At the start, it's all ok. The wrapper is hidden.
If I select 'A' and then 'C', for example, the wrapper disappear. BUT, if I select 'A' or 'B' and 'X' again (the first "choice", Select), the wrapper does NOT disappear!
I have put a breakpoint inside the setter of bean.val. The setter in invoked for all the choices BUT for the first one the value inside the debugger is the same as the previous one!
BUT! If I remove the validator, it works!
This is the validator:
#FacesValidator(value="requiredSelect")
public class RequiredSelect implements Validator {
protected MessageUtil messageUtil = new MessageUtil();
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String val = (String) value;
if ("X".equals(val)) {
FacesMessage msg = this.messageUtil.getDefaultValidationError();
throw new ValidatorException(msg);
}
}
}
Previously I used another validator:
#FacesValidator(value="requiredString")
public class RequiredString implements Validator {
protected MessageUtil messageUtil = new MessageUtil();
#Override
public void validate(
FacesContext context,
UIComponent component,
Object value
) throws ValidatorException {
String val = (String) value;
if (val == null || val.trim().isEmpty()) {
FacesMessage msg = this.messageUtil.getDefaultValidationError();
throw new ValidatorException(msg);
}
}
}
but it did not work, because the empty string was not saved to the backing bean at menu change. So, if, for example, you selected 'A' and then back '' (Select) and you try to submit the form, the select is signaled with errors, but the value of the select returns to be 'A'! So the bogus 'X' value.
I'm using Primefaces 3.4.1 and Mojarra 2.1.7
Your issue is a bit of a XY-problem. You are trying to hack your way out of creating a required p:selectOneMenu using the X select item. This requires you to create a validator. If you simply make the p:selectOneMenu required, and would use null as the itemValue of the placeholder, it prevents the need to handle the X, so you don't need a validator.
Selecting null will cause validation to fail. So, once a selection is made you no longer want to render the null placeholder. Normally one would use the hideNoSelectionOption attribute of the p:selectOneMenu. But this is not available in PrimeFaces 3.4.1. As you don't want to upgrade, you can add this behavior by creating a custom renderer for the SelectOneMenu.
Create a new class, say my.custom.MySelectOneMenuRenderer, and extend SelectOneMenuRenderer. In this you want to #Override the encodeInput method to something like:
protected SelectItem selectMeOption = new SelectItem(null, "Select");
#Override
protected void encodeInput(
FacesContext context, SelectOneMenu menu, String clientId,
List<SelectItem> selectItems, Object values, Object submittedValues,
Converter converter
) throws IOException {
String classes = menu.getStyleClass();
Pattern pattern = Pattern.compile("\\bnormalSelect\\b");
Matcher matcher = pattern.matcher(classes);
boolean isNormal = matcher.find();
Object value = menu.getValue();
// If there's not a class "normalSelect" and no value is set, add the
// null option as the first item
if (!isNormal) {
if (value == null) {
selectItems.add(0, selectMeOption);
}
else {
SelectItem firstOption = selectItems.get(0);
if (selectMeOption.equals(firstOption)) {
selectItems.remove(0);
}
}
}
super.encodeInput(
context, menu, clientId, selectItems, values, submittedValues,
converter
);
}
Add your custom renderer to the render-kit section in your faces-config.xml like:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.SelectOneMenuRenderer</renderer-type>
<renderer-class>my.custom.MySelectOneMenuRenderer</renderer-class>
</renderer>
</render-kit>
View example:
<p:selectOneMenu value="#{bean.val}" layout="pageDirection">
<f:selectItems value="#{bean.vals()}" />
<p:ajax update="#{empty bean.val ? '#this' : ''}" />
</p:selectOneMenu>
For a working example see: https://github.com/jepsar/primefaces-test/blob/so_68457571/src/main/webapp/test.xhtml
See also:
How to sort f:selectItems in each p:selectOneMenu of my application?
Please have a look at the JSF Life-Cycle for example here:
https://www.torsten-horn.de/img/JSF-Lifecycle.gif
And you will note, that if you throw "Validator-Exceptions" the "Update Model Values" Phase is skipped. (Process Events forwards to render response).
Hence your value "X", which you consider invalid in your validator is never send to the bean, and therefore the wrapper won't vanish.
What you actually want is:
Define a proper "NoSelect-Option"
Make the field "required".
Then - upon submission - the required field will be considered empty and throw a validator-exception where you need it.
ps.: A neat way to just bypass this "Issue" is to use hideNoSelectionOption on the selectOneMenu - that way, the form starts with "Please select", but the user can't switch back to "Please select", once he made an choice:
<p:selectOneMenu value="#{bean.val}" hideNoSelectionOption="#{not empty bean.val}">
<f:selectItem itemValue="#{null}" itemLabel="Select" noSelectionOption="true" />
<f:selectItems value="#{bean.getVals()}" />
<p:ajax update="#this,wrapper" process="#this" />
</p:selectOneMenu>
Updated, added #this to p:ajax.
PS: this does not work with old versions of Primefaces that does not have hideNoSelectionOption
Ok, this is my solution, thanks to the dognose's answer first part:
Change the input to:
<p:selectOneMenu value="#{bean.val}" required="true">
<f:selectItem itemValue="" itemLabel="Select" />
<f:selectItems value="#{bean.getVals()}" />
<p:ajax update="wrapper" />
</p:selectOneMenu>
No need to itemValue="#{none}", since null strings are automatically transformed to empty string by default, by jboss or primefaces, I do not remember. No need of noSelectionOption="true" too (I do not understand its scope... the validation works even if I don't put it)
added to a package my.package the files messages.properties, with this content:
javax.faces.component.UIInput.REQUIRED = Error
javax.faces.component.UIInput.REQUIRED_detail = One or more fields are missing or wrong. Please check the form
Then I added a p:messages like this one:
<p:messages id="messages" closable="true" showDetail="true" />
Then, I used this trick to remove the duplicate messages.
I'm using Primefaces version 5.3 autocomplete in a web project, I have written the search method and converter for the java entities i am searching for. These all work well the selected entity from the autocomplete is set correctly in the backing bean using a p:ajax tab and initially the entity name, which i specify in the itemValue, is set in the text input.
When i then submit the form that includes this autocomplete the variable in the backing bean which i set from the autocomplete stays as it is which is intended but the text input on the autocomplete will display the entities id instead of the name as i'd specified on the itemValue. This is because it is calling the toString method in my converter but I want it still to display the name yet i need the converter..
Has anyone come across this issue that may be able to help?
I have found a thread else where that describe this behaviour but it is a couple years old now and doesn't have an answer.
This thread is:
http://forum.primefaces.org/viewtopic.php?f=8&t=37918
As it may explain it better than i have...
Any help is appreciated.
UPDATE: code added
Here is my autocomplete tag, complete method returns a java List of entity type.
<h:form>
<p:autoComplete id="autocomplete" value="#{bean.selectedEntity}"
completeMethod="#{bean.listOfPossibleEntities}"
itemValue="#{_e}" itemLabel="#{_e.name}" autocomplete="off"
minQueryLength="3" var="_e"
placeholder="Enter Entity Name Here"
converter="EntityConverter" forceSelection="true">
<p:ajax event="itemSelect" update="enclosingForm"/>
<p:column>
<h:outputText value="#{_e.name}" />
</p:column>
</p:autoComplete>
<p:commandButton update="#form" >
</h:form>
FacesConverter looks like as below, i use a DAO call to our database find the object for each id
private EntityDAO entityDAO = (EntityDAO)Component.getInstance("entityDAO");
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Integer id = Integer.valueOf(value);
return entityDAO.findById(id,false);
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
String result = "";
if(object != null) {
if(object instanceof Entity){
result = ""+ String.valueOf(((Entity) object).getEntityId());
}
}
return result;
}
So yes on update of the h:form after the entity on the autocomplete is selected and the submitting with the p:commandButton the value display or the entity will change from the entity name to the entity id.
Hope this helps further thanks.
I would like to create a p:selectOneMenu item where the values and selected value display translated strings. So if the user has switched their language to french, the dropdown contents and selection will all be displayed in french, etc.
Currently I have a list of values to display, each of which knows what their string value is in each language (I realize this is probably an antipattern, will refactor once this is working.)
I've solved the issue of displaying the drop down elements in translated fashion using the following code. I've also created a subclass of the Omnifaces SelectItemsConverter class to get a translated string of each object.
<p:selectOneMenu
value="#{linkagecontroller.selectedLink}"
converter="linkageTypeSelectItemsConverter"
var="lnk">
<f:selectItems value="#{linkagecontroller.linkageTypes}"
var="item"/>
<p:column>
<div class="link-item-#{lnk.linkageTypeId}">
<h:outputText value="#{lnk.getKeyValue(language.localeCode)}" />
</div>
</p:column>
</p:selectOneMenu>
LinkageTypeSelectItemsConverter
#FacesConverter("linkageTypeSelectItemsConverter")
public class LinkageTypeSelectItemsConverter extends SelectItemsConverter implements Serializable {
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
final String locale = context.getApplication().evaluateExpressionGet(context,
"#{language.localeCode}",
String.class);
if (value instanceof LinkageTypeKey) {
return ((LinkageTypeKey) value).getKeyValue(locale);
} else {
return super.getAsString(context, component, value);
}
}
}
When the form is displayed, I can debug this converter, it's returning values translated, but the selected element ID is always being displayed in the default language, english. Thoughts?
The converter is for converting item values, not item labels.
You need to explicitly specify itemLabel.
<p:selectOneMenu
value="#{linkagecontroller.selectedLink}"
converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{linkagecontroller.linkageTypes}" var="item"
itemValue="#{item}" itemLabel="#{item.getKeyValue(language.localeCode)}" />
</p:selectOneMenu>
This is only a somewhat strange approach of localization. You usually put translations in a resource bundle instead of in the model itself. It could even be done without a converter if it's an enum.
See also:
How to use enum values in f:selectItem(s)
Localizing enum values in resource bundle.
Here's a snippet of the form I'm trying to submit:
<h:outputText value="Employees"></h:outputText>
<h:selectManyListbox id="employees"
value="#{lookupControl.memberEmployees}"
converter="#{employeeConverter}">
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectManyListbox>
<h:message id="employeesMsg"
for="employees"
errorStyle="color:red; display:block"
styleClass="errorMessage"/>
<h:outputText value="Project Lead"></h:outputText>
<h:selectOneListbox id="projectLead"
value="#{lookupControl.chosenLead}"
converter="#{employeeConverter}">
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectOneListbox>
<h:message id="projectLeadMsg"
for="projectLead"
errorStyle="color:red; display:block"
styleClass="errorMessage"/>
And here's the converter employeeConverter:
#FacesConverter(value = "employeeConverter")
public class EmployeeConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// hack to get an em
HelperBean helper = FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(context, "#{helperBean}", HelperBean.class);
EntityManager em = helper.getEm();
System.out.println(value);
Employee tmp = DBHelper.findEmployee(em, value);
return tmp;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
Employee tmp = (Employee) value;
return tmp.getEMPLOYEE_NUMBER();
}
}
The idea being that a "Project" (the object being created when this form is submitted) has a list of members (List<Employee>) and a team leader (Employee). When I try submitting the form, I get a message along the lines of:
Conversion Error setting value 'dbaccess.persistence.Employee[id=66666666]' for 'null Converter'.
It's saying it can't convert from a String to an Employee, but it worked for the field right above it. I'm a little confused.
The "null converter" is the exception that the converter instance cannot be found. Since you're referencing the converter as a managed bean by converter="#{employeeConverter}, it would only be found if it's annotated with #javax.faces.bean.ManagedBean
Add #ManagedBean(name = "employeeConverterBean") into class EmployeeConverter
#ManagedBean(name = "employeeConverterBean")
#FacesConverter(value = "employeeConverter")
public class EmployeeConverter implements Converter {
and use converter="#{employeeConverterBean}" instead of converter="#{employeeConverter}"
You are referring to a scoped variable in your converter= attribute, but #FacesConverter does not create a scoped variable. Rather, it registers your converter class with the converter ID you specify.
From the documentation for FacesConverter.value:
The value of this annotation attribute is taken to be the converter-id …
Remove the converter= attribute entirely from your h:selectManyListbox, and instead, add a nested f:converter element:
<h:selectManyListbox id="employees"
value="#{lookupControl.memberEmployees}">
<f:converter converterId="employeeConverter"/>
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectManyListbox>
Update: I'd always taken the tag documentation literally, which says the attribute's value "must evaluate to javax.faces.convert.Converter". BalusC points out that a nested f:converter is not needed; passing a literal converter ID in the converter= attribute will work:
<h:selectManyListbox id="employees"
value="#{lookupControl.memberEmployees}"
converter="employeeConverter">
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectManyListbox>
I had never felt comfortable doing that in the past, without a documented guarantee that it works. I just looked through the JSF specification and found this under its "Standard HTML RenderKit Tag Library" section:
The following action must be taken to handle the value of the converter property. If isLiteralText() on the converter property returns true, get the value of the property and treat it as a converterId by passing it as the argument to the createConverter() method of the Application instance for this webapp, then pass the created Converter to the setConverter() method of the component for this tag.
So yes, passing a converter-id in the converter= attribute is completely supported.
I'm Migrating My project from seam 2 to JSF 2.x .
In seam 2 for enum's we have
<s:convertEnum />
which will call the converter when correct value is selected not the noSelectionOption value.
In JSF 2.x I have created My custom Converter like
#Named("enumConverter")
#ApplicationScoped
public class EnumConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String key)
throws ConverterException {
if(!key.equalsIgnoreCase("-- Choose one --")){
// DO LOGIC
}
return null;
}
public String getAsString(FacesContext context, UIComponent component, Object object) {
}
Now the problem is in my view I'm using
the converter like
<h:selectOneMenu value="#{user.DocumentType}" required="true">
<f:selectItem itemLabel="-- Choose one --" itemValue="#{null}" noSelectionOption="true"/>
<f:selectItems value="${user.Constants(user.statuses)}"
var="Val" itemLabel="#{user.Display(Val)}"
/>
<f:converter converterId="enumConverter"></f:converter>
</h:selectOneMenu>
Here if i select the NoSelectionOption then also my converter is getting called ,,, which should not happen and it should be called only if correct value is needed
in case of seam 2 converter only if correct value is selected then only converter will be called otherwise "value is required" is displayed.
Kindly please tell me ,what I'm missing here ....
Appreciate and help ...
Thanks in advance
The above issue was due to a4j:commandlink which was having reRender attribute , i replaced it with render attribute and now message is getting displayed.
But now the issue is
*if required=true is not there and i select noSelectionOption then it will store blank value in DB.
is it necessary to provide a validator in jsf 2 for noSelectionOption ????
Thanks