user845854
user845854

Reputation: 81

F:selectItems showing the class not a value

I'm new to facelets and I have generated a project using netbeans but I struggling with the tag.

I have

<h:selectOneMenu id="country" value="#{organisation.organisation.country}" title="Country" >
                <f:selectItems value="#{country.countryItemsAvailableSelectOne}"/>
            </h:selectOneMenu>

In the select I get classpath.Country[iso=GB] which I can see is an object but I really want to see the the country.prinableName value. I've looked at this for half a day and have drawn a blank Thanks for any help

Upvotes: 2

Views: 6516

Answers (3)

sereja
sereja

Reputation: 1376

If you add editable="true" in your

<h:selectOneMenu value="#{bean.country}">

Then you'll get an unexpected String value (not from getAsString()) in the converter method:

public Object getAsObject(FacesContext context, UIComponent component, String value) { }

Upvotes: 0

BalusC
BalusC

Reputation: 1109532

Since you're talking about Facelets, I'll assume JSF 2.x.

To start, HTML is one and all String. JSF generates HTML. By default, non-String Java objects are by toString() method converted to their String representation while JSF generates the HTML. To properly convert between those Java objects and String, you need a Converter.

I assume that your Country object has already the equals() method properly implemented, otherwise validation will later fail with "Validation error: Value not valid" because the selected object doesn't return true on testing the equals() for any of the available items.

I'll also make a little change in the naming since #{country} is a confusing managed bean name because it does apparently not represent an instance of the Country class. I'll call it Data which should hold application wide data.

@ManagedBean
@ApplicaitionScoped
public class Data {

    private static final List<Country> COUNTRIES = populateItSomehow();

    public List<Country> getCountries() {
        return COUNTRIES;
    }

    // ...
}

I'll assume that the Country class has two properties code and name. I'll assume that the managed bean which receives the selected country has a private Country country property. In your <f:selectItems>, you need to loop over #{data.countries} and specify the country object as item value and the country name as item label.

<h:selectOneMenu value="#{bean.country}">
    <f:selectItems value="#{data.countries}" var="country" itemValue="#{country}" itemLabel="#{country.name}" />
</h:selectOneMenu>

Now, you need to create a Converter for the Country class. We'll convert based on the country code which is unique for every country (right?). In the getAsString() you implement code which converts the Java object to its unique String representation which is to be used in HTML. In getAsObject() you implement code which converts the unique HTML String representation back to the Java object.

@FacesConverter(forClass=Country.class)
public class CountryConverter implements Converter {

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        return (value instanceof Country) ? ((Country) value).getCode() : null;
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null) {
            return null;
        }

        Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);

        for (Country country : data.getCountries()) {
            if (country.getCode().equals(value)) {
                return country;
            }
        }

        throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Country", value)));
    }

}

The @FacesConverter will register it automatically in JSF and JSF will automatically use it whenever it encounters a value expression of the Country type. Ultimately, you end up with country code as item value and country name as item label. JSF will convert the submitted country code back to a fullworthy Country object upon form submission.

In JSF 1.x the principle is not much different. In this blog you can find two basic kickoff examples: Objects in h:selectOneMenu.

Upvotes: 8

Robin
Robin

Reputation: 8518

What happened to you, selectOneMenu call the toString() method for all given objects.

You've to use selectitems or a simple converter to manage that. A very simple example:

price.xhtml:

<h:selectOneMenu id="priceMenu" value="#{priceBean.selectedPrice}">
    <f:selectItems value="#{priceBean.prices}" />
</h:selectOneMenu>

PriceBean.java:

..
private String selectedPrice;
..
public String getSelectedPrice() {
    return selectedPrice;
}

public void setSelectedPrice(String newPrice) {
    selectedPrice = newPrice;
}
..
public List<SelectItem> getPrices() {
    List<SelectItem> retVal = new ArrayList<SelectItem>();

    retVal.add(new SelectItem("2"));
    retVal.add(new SelectItem("4"));
    retVal.add(new SelectItem("6"));

    return retVal;
}

Further informations about the SelectItem. If you want to use a specially object directly, for example an object called Price, you have to use a converter. Here an example is shown and here.

Upvotes: 1

Related Questions