7

Is it possible to have objects as itemValue in tag?

for example I have a class Foo:

public class Foo {
  private int id;
  private String name;
  private Date date;
}

And another class Bar

public class Bar {
  private Foo foos;
}

public class BarBean {
  private Set<Foo> foos;
}

Now in a Bean called BarBean I need to have a to get the Foo of the current Bar from User like this:

<h:selectOneMenu value="#{barBean.bar.foo}" required="true">
 <f:selectItems value="#{barBean.foos}"  var="foo" itemLabel="#{foo.name}" itemValue="#{foo}" />
</h:selectOneMenu>

---------------edited:

my converter:

package ir.khorasancustoms.g2g.converters;

import ir.khorasancustoms.g2g.persistance.CatalogValue;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

@FacesConverter("ir.khorasancustoms.CatalogValueConverter")
public class CatalogValueConverter implements Converter {

  @Override
  public Object getAsObject(FacesContext context, UIComponent component, String value) {
    SessionFactory factory = new Configuration().configure().buildSessionFactory();
    Session session = factory.openSession();

    try {
      int id = Integer.parseInt(value);
      CatalogValue catalogValue = (CatalogValue) session.load(CatalogValue .class, id);
      return catalogValue;
    } catch (Exception ex) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
      ResourceBundle rb = ResourceBundle.getBundle("application");
      String message = rb.getString("databaseConnectionFailed");
      FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, message, message));
    } finally {
      session.close();
    }

    return null;
  }

  @Override
  public String getAsString(FacesContext context, UIComponent component, Object value) {
    return ((CatalogValue) value).getId() + "";
  }

}

and my facelet:

    <h:outputText value="#{lbls.paymentUnit}:"/>
    <h:selectOneMenu id="paymentUnit" label="#{lbls.paymentUnit}" value="#{price.price.ctvUnit}" required="true">
      <f:selectItems value="#{price.paymentUnits}"/>
      <f:converter converterId="ir.khorasancustoms.CatalogValueConverter"/>
    </h:selectOneMenu>
    <h:message for="paymentUnit" infoClass="info" errorClass="error" warnClass="warning" fatalClass="fatal"/>
ehsun7b
  • 4,796
  • 14
  • 59
  • 98
  • OmniFaces implements a solution to use complex objects as itemValue using a custom Converter.. More info [here](http://showcase-omnifaces.rhcloud.com/showcase/converters/SelectItemsConverter.xhtml) –  Oct 08 '12 at 17:50

3 Answers3

6

Yes it is possible.

You need to write a converter that will convert Foo to SelectItem

Check implementation and very good article here

jmj
  • 237,923
  • 42
  • 401
  • 438
  • 3
    The article you point to is spot on, but your own summary is not completely right. The converter doesn't convert into a ``SelectItem``, but converts the model object (Foo here) into and back from a String representation. For this String representation the key or ID of the model object is taken, and to convert this ID back into an object a DAO is used. – Arjan Tijms Jan 08 '11 at 18:39
  • 1
    I created a converter according to the tutorial but it fails to set the bean value, I updated my question, take a look at my code please. – ehsun7b Jan 08 '11 at 18:45
2

BalusC's article as referenced by Jigar gives two excellent solutions. It's via a converter that uses a DAO or via Strings which get converted back and forth in the backing bean.

One additional solution is a hybrid of those two. Via e.g. an f:param you can give the converter access to an EL value expression that points to the Map in the backing bean BalusC mentioned. Instead of the DAO, which might need to access the DB for each conversion, the Map is consulted.

This allows you to use the full Foo object within the <f:selectItems> tag and saves you the possible call to the DB after each post back. The cost is however some extra complexity in coding as you have to provide the converter, the parameter and the map.

(I do have to add btw that such a Map works best when the backing bean is in the view scope or a larger scope. It won't really save you on DB calls when it's in request scope)

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
  • I see but it is a bit complex for me in the first touch with Converters, I just need a conversion between Object and ID which I think I've done, but it ends up in Validation error! :( – ehsun7b Jan 08 '11 at 18:53
  • @Ehsun: What validation error? "Value not valid"? If so, check [this answer](http://stackoverflow.com/questions/3039338/jsf-validation-error-shown-by-hmessage-while-updating-model-why/3039886#3039886). – BalusC Jan 08 '11 at 21:15
  • I didn't override the equals method, I solved the prob. Thanks – ehsun7b Jan 09 '11 at 06:00
1

I'm on Myfaces 2.0.2 and this works

<h:selectOneMenu value="#{barBean.bar.foo}" required="true">
 <f:selectItems value="#{barBean.foos}"  var="foo" itemLabel="#{foo.name}" itemValue="#{foo}" />
</h:selectOneMenu>

or even better

<h:selectOneMenu value="#{barBean.bar.foo}" required="true">
 <f:selectItems value="#{barBean.foos}"  />
</h:selectOneMenu>

works , with itemLabel defaulted to foo.toString()- Dunno if it matters but foos is a List<Foo> not a Set

The <f:converter> isnt necessary if using

@FacesConverter(forClass = Foo.class)

before the FooConverter

Manu de Hanoi
  • 272
  • 3
  • 14