I have a property which can possibly contain \n characters. I would like to check in the rendered attribute of a JSF component if the property contains \n and if so, then don't render the component. How can I achieve this?
actually the values is saved as null in database, but its coming as "\n\n" value in the service side.
Provided that you mean that the \n represents the sole value, then you can use fn:trim() for that.
<h:outputText value="#{bean.value}" rendered="#{not empty fn:trim(bean.value)}" />
Alternatively, create a converter which immediately trims the submitted value before the model get polluted with unwanted characters:
<h:inputTextarea value="#{bean.value}" converter="trimConverter" />
#FacesConverter("trimConverter")
public class TrimConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
String trimmed = (submittedValue != null) ? submittedValue.trim() : null;
return (trimmed == null || trimmed.isEmpty()) ? null : trimmed;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
return (modelValue != null) ? modelValue.toString() : "";
}
}
and then just do
<h:outputText value="#{bean.value}" rendered="#{not empty bean.value}" />
By the way, in the comments you specified escape="false". If this input is coming from an enduser via <textarea>, be aware that you're opening a huge XSS attack hole here. See also How to implement a possibility for user to post some html-formatted data in a safe way?
Related
I have problems understanding how to use selection in JSF 2 with POJO/entity effectively. For example, I'm trying to select a Warehouse entity via the below dropdown:
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>
And the below managed bean:
#Named
#ViewScoped
public class Bean {
private Warehouse selectedWarehouse;
private List<SelectItem> availableWarehouses;
// ...
#PostConstruct
public void init() {
// ...
availableWarehouses = new ArrayList<>();
for (Warehouse warehouse : warehouseService.listAll()) {
availableWarehouses.add(new SelectItem(warehouse, warehouse.getName()));
}
}
// ...
}
Notice that I use the whole Warehouse entity as the value of SelectItem.
When I submit the form, this fails with the following faces message:
Conversion Error setting value 'com.example.Warehouse#cafebabe' for 'null Converter'.
I was hoping that JSF could just set the correct Warehouse object to my managed bean when I wrap it in a SelectItem. Wrapping my entity inside the SelectItem was meant to skip creating a Converter for my entity.
Do I really have to use a Converter whenever I want to make use of entities in my <h:selectOneMenu>? It should for JSF be possible to just extract the selected item from the list of available items. If I really have to use a converter, what is the practical way of doing it? So far I came up to this:
Create a Converter implementation for the entity.
Overriding the getAsString(). I think I don't need this since the label property of the SelectItem will be used to display the dropdown option label.
Overriding the getAsObject(). I think this will be used to return the correct SelectItem or entity depending on the type of the selected field defined in the managed bean.
The getAsObject() confuses me. What is the efficient way to do this? Having the string value, how do I get the associated entity object? Should I query the entity object from the service object based on the string value and return the entity? Or perhaps somehow I can access the list of the entities that form the selection items, loop them to find the correct entity, and return the entity?
What is the normal approach of this?
Introduction
JSF generates HTML. HTML is in Java terms basically one large String. To represent Java objects in HTML, they have to be converted to String. Also, when a HTML form is submitted, the submitted values are treated as String in the HTTP request parameters. Under the covers, JSF extracts them from the HttpServletRequest#getParameter() which returns String.
To convert between a non-standard Java object (i.e. not a String, Number or Boolean for which EL has builtin conversions, or Date/LocalDate/ZonedDateTime for which JSF provides builtin <f:convertDateTime> tag), you really have to supply a custom Converter. The SelectItem has no special purpose at all. It's just a leftover from JSF 1.x when it wasn't possible to supply e.g. List<Warehouse> directly to <f:selectItems>. It has also no special treatment as to labels and conversion.
getAsString()
You need to implement getAsString() method in such way that the desired Java object is been represented in an unique String representation which can be used as HTTP request parameter. Normally, the technical ID (the database primary key) is used here.
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return ""; // Never return null here!
}
if (modelValue instanceof Warehouse) {
return String.valueOf(((Warehouse) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse"));
}
}
Note that returning an empty string in case of a null/empty model value is significant and required by the javadoc:
Returns: a zero-length String if value is null, otherwise the result of the conversion
Otherwise the generated <option> will not have a value attribute and by default send the item label back into getAsObject(). See also Using a "Please select" f:selectItem with null/empty value inside a p:selectOneMenu.
getAsObject()
You need to implement getAsObject() in such way that exactly that String representation as returned by getAsString() can be converted back to exactly the same Java object specified as modelValue in getAsString().
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return warehouseService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e);
}
}
In other words, you must be technically able to pass back the returned object as modelValue argument of getAsString() and then pass back the obtained string as submittedValue argument of getAsObject() in an infinite loop.
Usage
Finally just annotate the Converter with #FacesConverter to hook on the object type in question, JSF will then automatically take care of conversion when Warehouse type ever comes into the picture:
#FacesConverter(forClass=Warehouse.class)
That was the "canonical" JSF approach. It's after all not very effective as it could indeed also just have grabbed the item from the <f:selectItems>. But the most important point of a Converter is that it returns an unique String representation, so that the Java object could be identified by a simple String suitable for passing around in HTTP and HTML.
Generic converter based on toString()
JSF utility library OmniFaces has a SelectItemsConverter which works based on toString() outcome of the entity. This way you do not need to fiddle with getAsObject() and expensive business/database operations anymore. For some concrete use examples, see also the showcase.
To use it, just register it as below:
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
And make sure that the toString() of your Warehouse entity returns an unique representation of the entity. You could for instance directly return the ID:
#Override
public String toString() {
return String.valueOf(id);
}
Or something more readable/reusable:
#Override
public String toString() {
return "Warehouse[id=" + id + "]";
}
See also:
How to populate options of h:selectOneMenu from database?
Generic JSF entity converter - so that you don't need to write a converter for every entity.
Using enums in JSF selectitems - enums needs to be treated a bit differently
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Unrelated to the problem, since JSF 2.0 it's not explicitly required anymore to have a List<SelectItem> as <f:selectItem> value. Just a List<Warehouse> would also suffice.
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" var="warehouse"
itemLabel="#{warehouse.name}" itemValue="#{warehouse}" />
</h:selectOneMenu>
private Warehouse selectedWarehouse;
private List<Warehouse> availableWarehouses;
Example of JSF generic converter with ABaseEntity and identifier:
ABaseEntity.java
public abstract class ABaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
public abstract Long getIdentifier();
}
SelectItemToEntityConverter.java
#FacesConverter(value = "SelectItemToEntityConverter")
public class SelectItemToEntityConverter implements Converter {
#Override
public Object getAsObject(FacesContext ctx, UIComponent comp, String value) {
Object o = null;
if (!(value == null || value.isEmpty())) {
o = this.getSelectedItemAsEntity(comp, value);
}
return o;
}
#Override
public String getAsString(FacesContext ctx, UIComponent comp, Object value) {
String s = "";
if (value != null) {
s = ((ABaseEntity) value).getIdentifier().toString();
}
return s;
}
private ABaseEntity getSelectedItemAsEntity(UIComponent comp, String value) {
ABaseEntity item = null;
List<ABaseEntity> selectItems = null;
for (UIComponent uic : comp.getChildren()) {
if (uic instanceof UISelectItems) {
Long itemId = Long.valueOf(value);
selectItems = (List<ABaseEntity>) ((UISelectItems) uic).getValue();
if (itemId != null && selectItems != null && !selectItems.isEmpty()) {
Predicate<ABaseEntity> predicate = i -> i.getIdentifier().equals(itemId);
item = selectItems.stream().filter(predicate).findFirst().orElse(null);
}
}
}
return item;
}
}
And usage:
<p:selectOneMenu id="somItems" value="#{exampleBean.selectedItem}" converter="SelectItemToEntityConverter">
<f:selectItem itemLabel="< select item >" itemValue="#{null}"/>
<f:selectItems value="#{exampleBean.availableItems}" var="item" itemLabel="${item.identifier}" itemValue="#{item}"/>
</p:selectOneMenu>
I have the following element:
<p:selectOneListbox id="requirementsUrl"
value="#{data.selectedURL}">
<f:selectItems value="#{data.requirementsDocuments}" />
<f:validator validatorId="conf.ListValidator" />
</p:selectOneListbox>
inside my validator how I can check whether the size of the list requirementsDocuments is bigger than 0. data actually is a specific class generated by the framework that i am using and i cant directly access it through plain java code
In your validator you can access the value of the EL expression "#{data.requirementsDocuments}" via the FacesContext.
example:
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
List requireDocuments = context.getApplication()
.evaluateExpressionGet(context, "#{data.requirementsDocuments}", List.class);
if(requireDocuments.size() == 0) {
throw new ValidatorException("List is empty!");
}
}
I have problems understanding how to use selection in JSF 2 with POJO/entity effectively. For example, I'm trying to select a Warehouse entity via the below dropdown:
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>
And the below managed bean:
#Named
#ViewScoped
public class Bean {
private Warehouse selectedWarehouse;
private List<SelectItem> availableWarehouses;
// ...
#PostConstruct
public void init() {
// ...
availableWarehouses = new ArrayList<>();
for (Warehouse warehouse : warehouseService.listAll()) {
availableWarehouses.add(new SelectItem(warehouse, warehouse.getName()));
}
}
// ...
}
Notice that I use the whole Warehouse entity as the value of SelectItem.
When I submit the form, this fails with the following faces message:
Conversion Error setting value 'com.example.Warehouse#cafebabe' for 'null Converter'.
I was hoping that JSF could just set the correct Warehouse object to my managed bean when I wrap it in a SelectItem. Wrapping my entity inside the SelectItem was meant to skip creating a Converter for my entity.
Do I really have to use a Converter whenever I want to make use of entities in my <h:selectOneMenu>? It should for JSF be possible to just extract the selected item from the list of available items. If I really have to use a converter, what is the practical way of doing it? So far I came up to this:
Create a Converter implementation for the entity.
Overriding the getAsString(). I think I don't need this since the label property of the SelectItem will be used to display the dropdown option label.
Overriding the getAsObject(). I think this will be used to return the correct SelectItem or entity depending on the type of the selected field defined in the managed bean.
The getAsObject() confuses me. What is the efficient way to do this? Having the string value, how do I get the associated entity object? Should I query the entity object from the service object based on the string value and return the entity? Or perhaps somehow I can access the list of the entities that form the selection items, loop them to find the correct entity, and return the entity?
What is the normal approach of this?
Introduction
JSF generates HTML. HTML is in Java terms basically one large String. To represent Java objects in HTML, they have to be converted to String. Also, when a HTML form is submitted, the submitted values are treated as String in the HTTP request parameters. Under the covers, JSF extracts them from the HttpServletRequest#getParameter() which returns String.
To convert between a non-standard Java object (i.e. not a String, Number or Boolean for which EL has builtin conversions, or Date/LocalDate/ZonedDateTime for which JSF provides builtin <f:convertDateTime> tag), you really have to supply a custom Converter. The SelectItem has no special purpose at all. It's just a leftover from JSF 1.x when it wasn't possible to supply e.g. List<Warehouse> directly to <f:selectItems>. It has also no special treatment as to labels and conversion.
getAsString()
You need to implement getAsString() method in such way that the desired Java object is been represented in an unique String representation which can be used as HTTP request parameter. Normally, the technical ID (the database primary key) is used here.
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return ""; // Never return null here!
}
if (modelValue instanceof Warehouse) {
return String.valueOf(((Warehouse) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse"));
}
}
Note that returning an empty string in case of a null/empty model value is significant and required by the javadoc:
Returns: a zero-length String if value is null, otherwise the result of the conversion
Otherwise the generated <option> will not have a value attribute and by default send the item label back into getAsObject(). See also Using a "Please select" f:selectItem with null/empty value inside a p:selectOneMenu.
getAsObject()
You need to implement getAsObject() in such way that exactly that String representation as returned by getAsString() can be converted back to exactly the same Java object specified as modelValue in getAsString().
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return warehouseService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e);
}
}
In other words, you must be technically able to pass back the returned object as modelValue argument of getAsString() and then pass back the obtained string as submittedValue argument of getAsObject() in an infinite loop.
Usage
Finally just annotate the Converter with #FacesConverter to hook on the object type in question, JSF will then automatically take care of conversion when Warehouse type ever comes into the picture:
#FacesConverter(forClass=Warehouse.class)
That was the "canonical" JSF approach. It's after all not very effective as it could indeed also just have grabbed the item from the <f:selectItems>. But the most important point of a Converter is that it returns an unique String representation, so that the Java object could be identified by a simple String suitable for passing around in HTTP and HTML.
Generic converter based on toString()
JSF utility library OmniFaces has a SelectItemsConverter which works based on toString() outcome of the entity. This way you do not need to fiddle with getAsObject() and expensive business/database operations anymore. For some concrete use examples, see also the showcase.
To use it, just register it as below:
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
And make sure that the toString() of your Warehouse entity returns an unique representation of the entity. You could for instance directly return the ID:
#Override
public String toString() {
return String.valueOf(id);
}
Or something more readable/reusable:
#Override
public String toString() {
return "Warehouse[id=" + id + "]";
}
See also:
How to populate options of h:selectOneMenu from database?
Generic JSF entity converter - so that you don't need to write a converter for every entity.
Using enums in JSF selectitems - enums needs to be treated a bit differently
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Unrelated to the problem, since JSF 2.0 it's not explicitly required anymore to have a List<SelectItem> as <f:selectItem> value. Just a List<Warehouse> would also suffice.
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" var="warehouse"
itemLabel="#{warehouse.name}" itemValue="#{warehouse}" />
</h:selectOneMenu>
private Warehouse selectedWarehouse;
private List<Warehouse> availableWarehouses;
Example of JSF generic converter with ABaseEntity and identifier:
ABaseEntity.java
public abstract class ABaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
public abstract Long getIdentifier();
}
SelectItemToEntityConverter.java
#FacesConverter(value = "SelectItemToEntityConverter")
public class SelectItemToEntityConverter implements Converter {
#Override
public Object getAsObject(FacesContext ctx, UIComponent comp, String value) {
Object o = null;
if (!(value == null || value.isEmpty())) {
o = this.getSelectedItemAsEntity(comp, value);
}
return o;
}
#Override
public String getAsString(FacesContext ctx, UIComponent comp, Object value) {
String s = "";
if (value != null) {
s = ((ABaseEntity) value).getIdentifier().toString();
}
return s;
}
private ABaseEntity getSelectedItemAsEntity(UIComponent comp, String value) {
ABaseEntity item = null;
List<ABaseEntity> selectItems = null;
for (UIComponent uic : comp.getChildren()) {
if (uic instanceof UISelectItems) {
Long itemId = Long.valueOf(value);
selectItems = (List<ABaseEntity>) ((UISelectItems) uic).getValue();
if (itemId != null && selectItems != null && !selectItems.isEmpty()) {
Predicate<ABaseEntity> predicate = i -> i.getIdentifier().equals(itemId);
item = selectItems.stream().filter(predicate).findFirst().orElse(null);
}
}
}
return item;
}
}
And usage:
<p:selectOneMenu id="somItems" value="#{exampleBean.selectedItem}" converter="SelectItemToEntityConverter">
<f:selectItem itemLabel="< select item >" itemValue="#{null}"/>
<f:selectItems value="#{exampleBean.availableItems}" var="item" itemLabel="${item.identifier}" itemValue="#{item}"/>
</p:selectOneMenu>
I have problems understanding how to use selection in JSF 2 with POJO/entity effectively. For example, I'm trying to select a Warehouse entity via the below dropdown:
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>
And the below managed bean:
#Named
#ViewScoped
public class Bean {
private Warehouse selectedWarehouse;
private List<SelectItem> availableWarehouses;
// ...
#PostConstruct
public void init() {
// ...
availableWarehouses = new ArrayList<>();
for (Warehouse warehouse : warehouseService.listAll()) {
availableWarehouses.add(new SelectItem(warehouse, warehouse.getName()));
}
}
// ...
}
Notice that I use the whole Warehouse entity as the value of SelectItem.
When I submit the form, this fails with the following faces message:
Conversion Error setting value 'com.example.Warehouse#cafebabe' for 'null Converter'.
I was hoping that JSF could just set the correct Warehouse object to my managed bean when I wrap it in a SelectItem. Wrapping my entity inside the SelectItem was meant to skip creating a Converter for my entity.
Do I really have to use a Converter whenever I want to make use of entities in my <h:selectOneMenu>? It should for JSF be possible to just extract the selected item from the list of available items. If I really have to use a converter, what is the practical way of doing it? So far I came up to this:
Create a Converter implementation for the entity.
Overriding the getAsString(). I think I don't need this since the label property of the SelectItem will be used to display the dropdown option label.
Overriding the getAsObject(). I think this will be used to return the correct SelectItem or entity depending on the type of the selected field defined in the managed bean.
The getAsObject() confuses me. What is the efficient way to do this? Having the string value, how do I get the associated entity object? Should I query the entity object from the service object based on the string value and return the entity? Or perhaps somehow I can access the list of the entities that form the selection items, loop them to find the correct entity, and return the entity?
What is the normal approach of this?
Introduction
JSF generates HTML. HTML is in Java terms basically one large String. To represent Java objects in HTML, they have to be converted to String. Also, when a HTML form is submitted, the submitted values are treated as String in the HTTP request parameters. Under the covers, JSF extracts them from the HttpServletRequest#getParameter() which returns String.
To convert between a non-standard Java object (i.e. not a String, Number or Boolean for which EL has builtin conversions, or Date/LocalDate/ZonedDateTime for which JSF provides builtin <f:convertDateTime> tag), you really have to supply a custom Converter. The SelectItem has no special purpose at all. It's just a leftover from JSF 1.x when it wasn't possible to supply e.g. List<Warehouse> directly to <f:selectItems>. It has also no special treatment as to labels and conversion.
getAsString()
You need to implement getAsString() method in such way that the desired Java object is been represented in an unique String representation which can be used as HTTP request parameter. Normally, the technical ID (the database primary key) is used here.
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return ""; // Never return null here!
}
if (modelValue instanceof Warehouse) {
return String.valueOf(((Warehouse) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse"));
}
}
Note that returning an empty string in case of a null/empty model value is significant and required by the javadoc:
Returns: a zero-length String if value is null, otherwise the result of the conversion
Otherwise the generated <option> will not have a value attribute and by default send the item label back into getAsObject(). See also Using a "Please select" f:selectItem with null/empty value inside a p:selectOneMenu.
getAsObject()
You need to implement getAsObject() in such way that exactly that String representation as returned by getAsString() can be converted back to exactly the same Java object specified as modelValue in getAsString().
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return warehouseService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e);
}
}
In other words, you must be technically able to pass back the returned object as modelValue argument of getAsString() and then pass back the obtained string as submittedValue argument of getAsObject() in an infinite loop.
Usage
Finally just annotate the Converter with #FacesConverter to hook on the object type in question, JSF will then automatically take care of conversion when Warehouse type ever comes into the picture:
#FacesConverter(forClass=Warehouse.class)
That was the "canonical" JSF approach. It's after all not very effective as it could indeed also just have grabbed the item from the <f:selectItems>. But the most important point of a Converter is that it returns an unique String representation, so that the Java object could be identified by a simple String suitable for passing around in HTTP and HTML.
Generic converter based on toString()
JSF utility library OmniFaces has a SelectItemsConverter which works based on toString() outcome of the entity. This way you do not need to fiddle with getAsObject() and expensive business/database operations anymore. For some concrete use examples, see also the showcase.
To use it, just register it as below:
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
And make sure that the toString() of your Warehouse entity returns an unique representation of the entity. You could for instance directly return the ID:
#Override
public String toString() {
return String.valueOf(id);
}
Or something more readable/reusable:
#Override
public String toString() {
return "Warehouse[id=" + id + "]";
}
See also:
How to populate options of h:selectOneMenu from database?
Generic JSF entity converter - so that you don't need to write a converter for every entity.
Using enums in JSF selectitems - enums needs to be treated a bit differently
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Unrelated to the problem, since JSF 2.0 it's not explicitly required anymore to have a List<SelectItem> as <f:selectItem> value. Just a List<Warehouse> would also suffice.
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" var="warehouse"
itemLabel="#{warehouse.name}" itemValue="#{warehouse}" />
</h:selectOneMenu>
private Warehouse selectedWarehouse;
private List<Warehouse> availableWarehouses;
Example of JSF generic converter with ABaseEntity and identifier:
ABaseEntity.java
public abstract class ABaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
public abstract Long getIdentifier();
}
SelectItemToEntityConverter.java
#FacesConverter(value = "SelectItemToEntityConverter")
public class SelectItemToEntityConverter implements Converter {
#Override
public Object getAsObject(FacesContext ctx, UIComponent comp, String value) {
Object o = null;
if (!(value == null || value.isEmpty())) {
o = this.getSelectedItemAsEntity(comp, value);
}
return o;
}
#Override
public String getAsString(FacesContext ctx, UIComponent comp, Object value) {
String s = "";
if (value != null) {
s = ((ABaseEntity) value).getIdentifier().toString();
}
return s;
}
private ABaseEntity getSelectedItemAsEntity(UIComponent comp, String value) {
ABaseEntity item = null;
List<ABaseEntity> selectItems = null;
for (UIComponent uic : comp.getChildren()) {
if (uic instanceof UISelectItems) {
Long itemId = Long.valueOf(value);
selectItems = (List<ABaseEntity>) ((UISelectItems) uic).getValue();
if (itemId != null && selectItems != null && !selectItems.isEmpty()) {
Predicate<ABaseEntity> predicate = i -> i.getIdentifier().equals(itemId);
item = selectItems.stream().filter(predicate).findFirst().orElse(null);
}
}
}
return item;
}
}
And usage:
<p:selectOneMenu id="somItems" value="#{exampleBean.selectedItem}" converter="SelectItemToEntityConverter">
<f:selectItem itemLabel="< select item >" itemValue="#{null}"/>
<f:selectItems value="#{exampleBean.availableItems}" var="item" itemLabel="${item.identifier}" itemValue="#{item}"/>
</p:selectOneMenu>
I'm populating a <p:selectOneMenu/> from database as follows.
<p:selectOneMenu id="cmbCountry"
value="#{bean.country}"
required="true"
converter="#{countryConverter}">
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
<f:selectItems var="country"
value="#{bean.countries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
<p:message for="cmbCountry"/>
The default selected option, when this page is loaded is,
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
The converter:
#ManagedBean
#ApplicationScoped
public final class CountryConverter implements Converter {
#EJB
private final Service service = null;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
//Returns the item label of <f:selectItem>
System.out.println("value = " + value);
if (!StringUtils.isNotBlank(value)) {
return null;
} // Makes no difference, if removed.
long parsedValue = Long.parseLong(value);
if (parsedValue <= 0) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Message"));
}
Country entity = service.findCountryById(parsedValue);
if (entity == null) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "", "Message"));
}
return entity;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Message"), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value instanceof Country ? ((Country) value).getCountryId().toString() : null;
}
}
When the first item from the menu represented by <f:selectItem> is selected and the form is submitted then, the value obtained in the getAsObject() method is Select which is the label of <f:selectItem> - the first item in the list which is intuitively not expected at all.
When the itemValue attribute of <f:selectItem> is set to an empty string then, it throws java.lang.NumberFormatException: For input string: "" in the getAsObject() method even though the exception is precisely caught and registered for ConverterException.
This somehow seems to work, when the return statement of the getAsString() is changed from
return value instanceof Country?((Country)value).getCountryId().toString():null;
to
return value instanceof Country?((Country)value).getCountryId().toString():"";
null is replaced by an empty string but returning an empty string when the object in question is null, in turn incurs another problem as demonstrated here.
How to make such converters work properly?
Also tried with org.omnifaces.converter.SelectItemsConverter but it made no difference.
When the select item value is null, then JSF won't render <option value>, but only <option>. As consequence, browsers will submit the option's label instead. This is clearly specified in HTML specification (emphasis mine):
value = cdata [CS]
This attribute specifies the initial value of the control. If this attribute is not set, the initial value is set to the contents of the OPTION element.
You can also confirm this by looking at HTTP traffic monitor. You should see the option label being submitted.
You need to set the select item value to an empty string instead. JSF will then render a <option value="">. If you're using a converter, then you should actually be returning an empty string "" from the converter when the value is null. This is also clearly specified in Converter#getAsString() javadoc (emphasis mine):
getAsString
...
Returns: a zero-length String if value is null, otherwise the result of the conversion
So if you use <f:selectItem itemValue="#{null}"> in combination with such a converter, then a <option value=""> will be rendered and the browser will submit just an empty string instead of the option label.
As to dealing with the empty string submitted value (or null), you should actually let your converter delegate this responsibility to the required="true" attribute. So, when the incoming value is null or an empty string, then you should return null immediately. Basically your entity converter should be implemented like follows:
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return ""; // Required by spec.
}
if (!(value instanceof SomeEntity)) {
throw new ConverterException("Value is not a valid instance of SomeEntity.");
}
Long id = ((SomeEntity) value).getId();
return (id != null) ? id.toString() : "";
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null; // Let required="true" do its job on this.
}
if (!Utils.isNumber(value)) {
throw new ConverterException("Value is not a valid ID of SomeEntity.");
}
Long id = Long.valueOf(value);
return someService.find(id);
}
As to your particular problem with this,
but returning an empty string when the object in question is null, in turn incurs another problem as demonstrated here.
As answered over there, this is a bug in Mojarra and bypassed in <o:viewParam> since OmniFaces 1.8. So if you upgrade to at least OmniFaces 1.8.3 and use its <o:viewParam> instead of <f:viewParam>, then you shouldn't be affected anymore by this bug.
The OmniFaces SelectItemsConverter should also work as good in this circumstance. It returns an empty string for null.
If you want to avoid null values for your select component, the most elegant way is to use the noSelectionOption.
When noSelectionOption="true", the converter will not even try to process the value.
Plus, when you combine that with <p:selectOneMenu required="true"> you will get a validation error, when user tries to select that option.
One final touch, you can use the itemDisabled attribute to make it clear to the user that he can't use this option.
<p:selectOneMenu id="cmbCountry"
value="#{bean.country}"
required="true"
converter="#{countryConverter}">
<f:selectItem itemLabel="Select"
noSelectionOption="true"
itemDisabled="true"/>
<f:selectItems var="country"
value="#{bean.countries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
<p:message for="cmbCountry"/>
Now if you do want to be able to set a null value, you can 'cheat' the converter to return a null value, by using
<f:selectItem itemLabel="Select" itemValue="" />
More reading here, here, or here
You're mixing a few things, and it's not fully clear to me what you want to achieve, but let's try
This obviously causes the java.lang.NumberFormatException to be thrown
in its converter.
It's nothing obvious in it. You don't check in converter if value is empty or null String, and you should. In that case the converter should return null.
Why does it render Select (itemLabel) as its value and not an empty
string (itemValue)?
The select must have something selected. If you don't provide empty value, the first element from list would be selected, which is not something that you would expect.
Just fix the converter to work with empty/null strings and let the JSF react to returned null as not allowed value. The conversion is called first, then comes the validation.
I hope that answers your questions.
In addition to incompleteness, this answer was deprecated, since I was using Spring at the time of this post :
I have modified the converter's getAsString() method to return an empty string instead of returning null, when no Country object is found like (in addition to some other changes),
#Controller
#Scope("request")
public final class CountryConverter implements Converter {
#Autowired
private final transient Service service = null;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
long parsedValue = Long.parseLong(value);
if (parsedValue <= 0) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "The id cannot be zero or negative."));
}
Country country = service.findCountryById(parsedValue);
if (country == null) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "", "The supplied id doesn't exist."));
}
return country;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Conversion error : Incorrect id."), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value instanceof Country ? ((Country) value).getCountryId().toString() : ""; //<--- Returns an empty string, when no Country is found.
}
}
And <f:selectItem>'s itemValue to accept a null value as follows.
<p:selectOneMenu id="cmbCountry"
value="#{stateManagedBean.selectedItem}"
required="true">
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
<f:selectItems var="country"
converter="#{countryConverter}"
value="#{stateManagedBean.selectedItems}"
itemLabel="#{country.countryName}"
itemValue="${country}"/>
</p:selectOneMenu>
<p:message for="cmbCountry"/>
This generates the following HTML.
<select id="form:cmbCountry_input" name="form:cmbCountry_input">
<option value="" selected="selected">Select</option>
<option value="56">Country1</option>
<option value="55">Country2</option>
</select>
Earlier, the generated HTML looked like,
<select id="form:cmbCountry_input" name="form:cmbCountry_input">
<option selected="selected">Select</option>
<option value="56">Country1</option>
<option value="55">Country2</option>
</select>
Notice the first <option> with no value attribute.
This works as expected bypassing the converter when the first option is selected (even though require is set to false). When itemValue is changed to other than null, then it behaves unpredictably (I don't understand this).
No other items in the list can be selected, if it is set to a non-null value and the item received in the converter is always an empty string (even though another option is selected).
Additionally, when this empty string is parsed to Long in the converter, the ConverterException which is caused after the NumberFormatException is thrown doesn't report the error in the UIViewRoot (at least this should happen). The full exception stacktrace can be seen on the server console instead.
If someone could expose some light on this, I would accept the answer, if it is given.
This is fully working to me :
<p:selectOneMenu id="cmbCountry"
value="#{bean.country}"
required="true"
converter="#{countryConverter}">
<f:selectItem itemLabel="Select"/>
<f:selectItems var="country"
value="#{bean.countries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
The Converter
#Controller
#Scope("request")
public final class CountryConverter implements Converter {
#Autowired
private final transient Service service = null;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.trim().equals("")) {
return null;
}
//....
// No change
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value == null ? null : value instanceof Country ? ((Country) value).getCountryId().toString() : null;
//**** Returns an empty string, when no Country is found ---> wrong should return null, don't care about the rendering.
}
}
public void limparSelecao(AjaxBehaviorEvent evt) {
Object submittedValue = ((UIInput)evt.getSource()).getSubmittedValue();
if (submittedValue != null) {
getPojo().setTipoCaixa(null);
}
}
<p:selectOneMenu id="tipo"
value="#{cadastroCaixaMonitoramento.pojo.tipoCaixa}"
immediate="true"
required="true"
valueChangeListener="#{cadastroCaixaMonitoramento.selecionarTipoCaixa}">
<f:selectItem itemLabel="Selecione" itemValue="SELECIONE" noSelectionOption="false"/>
<f:selectItems value="#{cadastroCaixaMonitoramento.tiposCaixa}"
var="tipo" itemValue="#{tipo}"
itemLabel="#{tipo.descricao}" />
<p:ajax process="tipo"
update="iten_monitorado"
event="change" listener="#{cadastroCaixaMonitoramento.limparSelecao}" />
</p:selectOneMenu>