I have an enum whose code is like this -
public enum COSOptionType {
NOTAPPLICABLE,
OPTIONAL,
MANDATORY;
private String[] label = { "Not Applicable", "Optional", "Mandatory"};
#Override
public String toString() {
return label[this.ordinal()];
}
public static COSOptionType getCOSOption(String value) {
int ivalue = Integer.parseInt(value);
switch(ivalue) {
case 0:
return NOTAPPLICABLE;
case 1:
return OPTIONAL;
case 2:
return MANDATORY;
default:
throw new RuntimeException("Should not get this far ever!");
}
}
}
I have the converter to convert the enum type
public class COSEnumConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent comp, String value) {
return COSOptionType.getCOSOption(value);
}
public String getAsString(FacesContext context, UIComponent comp, Object obj) {
if (obj instanceof String) {
return (String) obj;
}
COSOptionType type = (COSOptionType) obj;
int index = type.ordinal();
return ""+index;
}
}
The view looks like this
<h:selectOneMenu value="#{controller.type}" id="smoking">
<f:selectItems value="#{jnyController.choices}" />
</h:selectOneMenu>
Here is the code for create choices
private List<SelectItem> createChoicies() {
List<SelectItem> list = new ArrayList<SelectItem>();
for (COSOptionType cos : COSOptionType.values()) {
SelectItem item = new SelectItem();
item.setLabel(cos.toString());
item.setValue("" + cos.ordinal());
list.add(item);
}
return list;
}
I do not understand why this would throw "validation error" all the time ? I can debug and see that the converter is working fine.
NOTE: I am using JSF 1.1
The root cause is that the converter returns a fullworthy COSOptionType object instead of the string "" + cos.ordinal() in getAsObject(), which thus don't appear at all in the existing options.
At any way, this approach is overcomplicated, especially the enum and the converter. I recommend you to reconsider this approach as follows:
Option:
package com.example;
public enum Option {
NOTAPPLICABLE("Not Applicable"),
OPTIONAL("Optional"),
MANDATORY("Mandatory");
private String label;
private Option(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
Bean:
package com.example;
import java.util.ArrayList;
import java.util.List;
import javax.faces.model.SelectItem;
public class Bean {
private List<SelectItem> options = new ArrayList<SelectItem>();
private Option option;
public Bean() {
for (Option option : Option.values()) {
options.add(new SelectItem(option, option.getLabel()));
}
}
public void submit() {
System.out.println(option);
}
public List<SelectItem> getOptions() {
return options;
}
public Option getOption() {
return option;
}
public void setOption(Option option) {
this.option = option;
}
}
OptionConverter:
package com.example;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
public class OptionConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return Option.valueOf(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return ((Option) value).name();
}
}
faces-config.xml:
<managed-bean>
<managed-bean-name>bean</managed-bean-name>
<managed-bean-class>com.example.Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<converter>
<converter-class>com.example.OptionConverter</converter-class>
<converter-for-class>com.example.Option</converter-for-class>
</converter>
Relevant part of JSF page:
<h:form>
<h:selectOneMenu value="#{bean.option}">
<f:selectItems value="#{bean.options}" />
</h:selectOneMenu>
<h:commandButton value="submit" action="#{bean.submit}" />
<h:messages />
</h:form>
No need to hassle with Enum#ordinal() which is considered evil.
If you were using JSF 1.2 or newer which ships with a builtin generic EnumConverter, the OptionConverter is totally superfluous and can be safely removed.
Try item.setValue(cos) instead of item.setValue("" + cos.ordinal()).
Related
I would like to add one or more object from a list of object into an other empty list. I am sure the first list is already initialized as I am able to print it in the p:selectManyCheckbox.
I think it does not copy the object Comite into the list but just the string as I get the error : java.lang.ClassCastException: java.lang.String cannot be cast to com.primasatis.jsf.model.Comite
Thus how can I copy it into the list ?
public class Comite {
private int idComite;
private int idPerimetre;
private String comite;
public Comite(int idComite, int idPerimetre, String comite) {
super();
this.idComite = idComite;
this.idPerimetre = idPerimetre;
this.comite = comite;
}
}
public static boolean addUserComiteRelation(int idUser, List<Comite> comites) {
System.out.println(comites.size()); // print the good number
for(int i=0; i<comites.size();i++) {
System.out.println(comites.get(i).comite); //error here
}
}
<h:form>
<p:outputLabel for="Comites" value="Comités" />
<p:selectManyCheckbox id="Comites" value="#{utilisateur.comites}"
required="true"
requiredMessage="L'utilisateur doit appartenir à au moins un comité">
<f:selectItems value="#{sessionScope.utilisateur.comites}" />
</p:selectManyCheckbox>
<h:message for="Comites"></h:message>
</h:form>
really quick view of my class utilisateur (#Named and #SessionScoped)
public class Utilisateur implements Serializable {
private Perimetre perimetreUtilisateur = null;
private List<Comite> comites = null;
#PostConstruct
private void init() {
perimetreUtilisateur = new Perimetre();
comites = new ArrayList<>();
}
}
The usual way to use p:selectManyCheckbox is to provide a List of javax.faces.model.SelectItem objects.
If you want to use your own Pojos as options in p:selectManyCheckbox, then you would have to write a Converter ( javax.faces.convert.FacesConverter ).
For Example, your converter would look like :
In My ManagedBean(Example: ComiteManagedBean) class I would keep all Comite instances in a MAP(Example: comiteMap) with Comite.idComite as Key.
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter("comiteConverter")
public class ComiteConverter implements Converter {
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
try {
Comite comite = (ComiteManagedBean) fc.getExternalContext().getApplicationMap().get("ComiteManagedBean");
return service.getComiteMap().get(Integer.parseInt(value));
} catch(NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid theme."));
}
}
else {
return null;
}
}
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if(object != null) {
return ((Comite) object).getComite();
}
else {
return null;
}
}
}
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;
}
Is it possible to add validators to inputText fields from outside the h:inputText tag?
<!-- instead of this -->
<h:inputText id="field1" value="#{backingBean.field1}">
<f:validator validatorId="CustomValidator" />
</h:inputText>
<!-- something like this -->
<h:inputText id="field1" value="#{backingBean.field1}" />
<abc:validations>
<abc:validation for="field1" validator="CustomValidatorName" />
</abc:validations>
The abc:validation is a really short custom component
<cc:interface componentType="validation">
<cc:attribute name="for" />
<cc:attribute name="validator" />
</cc:interface>
<cc:implementation>
</cc:implementation>
</ui:composition>
and a faces component class
package at.sozvers.ecm.webclient.frontend.component;
import java.io.Serializable;
import javax.faces.component.FacesComponent;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UINamingContainer;
import javax.faces.validator.Validator;
#FacesComponent("validation")
public class Validation extends UIComponentBase implements NamingContainer, Serializable {
private static final long serialVersionUID = 1L;
private String for1;
private Validator validator;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public String getFor() {
return for1;
}
public void setFor(String for1) {
this.for1 = for1;
}
public Validator getValidator() {
return validator;
}
public void setValidator(Validator validator) {
this.validator = validator;
}
}
I read about TagHandlers and ComponentHandlers but I have no idea how to start.
I found a solution
I created a class extending ComponentHandlerWrapper and override the onComponentPopulated method where I read the ViewMap and put a ValidatorSet for each inputText id that has to be validated. The ValidatorSet contains a Set where I write the FacesValidator names into.
public class ParentAddingComponentHandlerWrapper extends ComponentHandlerWrapper {
#Override
public void onComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent) {
super.onComponentPopulated(ctx, c, parent);
Map<String, Object> viewMap = ctx.getFacesContext().getViewRoot().getViewMap();
if (c instanceof Validation) { // Validation is the FacesComponent class for <abc:validation>
TagAttribute uiInputName = getAttribute("for");
if (null == viewMap.get(uiInputName.getValue())) {
viewMap.put(uiInputName.getValue(), new ValidatorSet());
}
TagAttribute validator = getAttribute("validator"); // the validator attribute contains a FacesValidator name
ValidatorSet validators = (ValidatorSet) viewMap.get(uiInputName.getValue());
if (validator != null) {
validators.addValidator(validator.getValue());
}
}
}
Than I created a CustomComponent where I'm "decorating" a inputText. I also created a componentType/FacesComponent named field which I annotated with #ListenerFor(systemEventClass = PreValidateEvent.class)
Than I had to override the processEvent method where I added the FacesValidator to the UIInput component
#Override
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
super.processEvent(event);
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
String id = getAttributeValue("id", "");
ValidatorSet validators = (ValidatorSet) viewMap.get(id);
if (validators != null) {
UIComponent findComponent = findComponent(id);
if (findComponent instanceof UIInput) {
UIInput input = (UIInput) findComponent;
List<Validator> inputValidators = Arrays.asList(input.getValidators());
for (Validator validator : validators.getValidators()) {
if (!doesUIInputContainsValidator(inputValidators, validator)) {
input.addValidator(validator);
}
}
}
}
}
private boolean doesUIInputContainsValidator(List<Validator> inputValidators, Validator validator) {
for (Validator inputValidator : inputValidators) {
if (inputValidator.getClass().isInstance(validator)) {
return true;
}
}
return false;
}
This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 7 years ago.
I use primefaces's p:selectManyMenu. When I submit the value, it always report validation error Illegal selection options. Can you help to solve this issue?
JSF code:
<h:outputLabel for="scenario" value="GivenStories:" />
<p:selectManyMenu id="givenstoryselect" value="#{anotherDynaFormController.selectedGivenStory}" converter="scenarioConverter" var="g" style="width:500px;height:70px;align:left;" showCheckbox="true" >
<f:selectItems value="#{anotherDynaFormController.givenStorys}" var="scenario" itemLabel="#{scenario.name}" itemValue="#{scenario}" />
<p:column>#{g.name}</p:column>
</p:selectManyMenu>
Bean class:
//converter
#FacesConverter(value="scenarioConverter")
public class ScenarioConverter implements Converter {
public static List<Scenario> scenarioDB = new ArrayList<Scenario>();
private ScenarioDao scenarioDao = new ScenarioDao();
public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
if (submittedValue.trim().equals("")) {
return null;
} else {
try {
int number = Integer.parseInt(submittedValue);
scenarioDB = scenarioDao.findAll();
for (Scenario p : scenarioDB) {
if (p.getId() == number) {
return p;
}
}
} catch(NumberFormatException exception) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid scenario"));
}
}
return null;
}
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
if (value == null) {
return null;
} else {
return String.valueOf(((Scenario) value).getId());
}
}
}
#ManagedBean
#ViewScoped
public class AnotherDynaFormController extends SuperBean implements Serializable {
private List<Scenario> givenStorys;
private List<Meta> selectedMetas;
private List<Scenario> selectedGivenStory;
private List<ProjectTree> parents;
public DynaFormModel getModel() {
givenStorys = scenarioDao.findAll();
...
}
...
}
Try this generic converter and see if problem still there .
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {
private static Map<Object, String> entities = new WeakHashMap<Object, String>();
#Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
synchronized (entities) {
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
for (Entry<Object, String> entry : entities.entrySet()) {
if (entry.getValue().equals(uuid)) {
return entry.getKey();
}
}
return null;
}
}
thanks a lot. I have find the reason of this issue. I should overwrite the equals and hashcode method in this object. Then it works.
#Override
public boolean equals(Object obj) {
if(obj == null)
return false;
if(!(obj instanceof Scenario))
return false;
return ((Scenario)obj).getId().equals(this.id);
}
#Override
public int hashCode() {
int hash = 1;
return hash * 31 + name.hashCode();
}
Using the following Bean, I fill a form with countries:
#ManagedBean
#RequestScoped
public class CreateUser {
#EJB
private ParticipantDAO participantDAO;
#EJB
private CountryDAO countryDAO;
private List<Country> countries = new ArrayList<Country>();
. . .
. . .
. . .
#PostConstruct
public void init() {
countries = countryDAO.getAllCountries();
}
In the form I've to use a Converter:
<h:selectOneMenu id="country" value="#{createUser.user.country}" required="true" requiredMessage="Please select a country." converter="#{countryConverter}" >
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{createUser.countries}" var="country" itemValue="#{country}" itemLabel="#{country.country}" />
</h:selectOneMenu>
The Converter give a NullPointerException seems because it's unable to inject CountryDAO:
#ManagedBean
#RequestScoped
#FacesConverter(forClass = Country.class)
public class CountryConverter implements Converter {
#EJB
private CountryDAO countryDAO;
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Country)) {
return null;
}
return String.valueOf(((Country) value).getId());
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
System.out.println("Converter Value: " + value);
Country c = countryDAO.find(Long.valueOf(value));
System.out.println("Converter: " + c.getCountry());
return c;
} catch (Exception e) {
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Country %s %d", value, e.toString(), Long.valueOf((value)))), e);
}
}
}
In the console I see the "Converted Value" message but not the "CountryDAO find" that should be printed by createDAO.find method.
#Stateless
#LocalBean
public class CountryDAO {
public CountryDAO() {
}
#PersistenceContext
private EntityManager em;
#Resource
SessionContext context;
public List<Country> getAllCountries() {
TypedQuery<Country> query = em.createNamedQuery(Country.FIND_ALL, Country.class);
return query.getResultList();
}
public Country find(Long id) {
System.out.println("CountryDAO find");
Country c = em.find(Country.class, id);
System.out.println(c.getCountry());
return c;
}
I tried the solution reported to Inject a EJB into a JSF converter with JEE6 (I don't know if I put the code in the correct location). I put it in the converter (and I obtain the NullPointerException):
#ManagedBean
#FacesConverter(forClass = Country.class)
public class CountryConverter implements Converter {
// #EJB
// private CountryDAO countryDAO;
private InitialContext ic;
private CountryDAO countryDAO;
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Country)) {
return null;
}
return String.valueOf(((Country) value).getId());
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
System.out.println("Converter Value: " + value);
try {
try {
ic = new InitialContext();
countryDAO = (CountryDAO) ic.lookup("java:global/DAO/CountryDAO");
} catch (NamingException e) {
}
Country c = countryDAO.find(Long.valueOf(value));
System.out.println("Converter: " + c.getCountry());
return c;
} catch (Exception e) {
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Country %s %d", value, e.toString(), Long.valueOf((value)))), e);
}
}
I was having the same problem in the some other project, where I was using a converter for some primefaces component. I solved the problem the CDI way.
All you have to do is annotate your converter class with #Named (and inject the DAO class via #Inject (JEE6), and not with JEE5 - #EJB).
You reference your converter with binding attribute like:
<f:converter binding="#{countryConverter}" />