Friday, September 7, 2007

Objects in h:selectOneMenu

WARNING - OUTDATED CONTENT!

This article is targeted on JSF 1.2. For JSF 2.0, using objects in UISelectOne and UISelectMany components can be approached much more elegantly with help of OmniFaces SelectItemsConverter without the need to write a custom converter.

A HTML response is in Object Oriented perspective nothing less or more than one big String value (technically: a long character array). Non-String-typed Java Objects are forced to their String representation using Object#toString() when they are about to be written to the HTML response. That's why using custom Object types in a UISelectOne or UISelectMany component, for example h:selectOneMenu, can drive unaware JSF developers nuts. If you gently have attached a <h:message /> to the component for error messages, or added the <h:messages /> tag to the page for debugging purposes, then you will likely get the following error message:

That roughly means that JSF cannot convert the given String value "mypackage.Foo@1234567" (which is just the String representation of the Object, obtained by Object#toString()) to the Object type which is expected by the valuebinding of the component, e.g. mypackage.Foo. The message also indicates that JSF couldn't find a suitable converter for it, pointing to the null ID in the 'null Converter' message part. Generally this will only occur if those objects are not of a String type and JSF does not have a built-in converter for it as well. That's why, next to plain String values, for example the types of the Number superclass just works in the h:selectOneMenu. At least in the newer builds of JSF, the 1.1_02 and 1.2_02 or later, due to some coerce bugs with Number types in SelectItem in the older builds.

There are two general solutions for this conversion problem: 1) implement javax.faces.convert.Converter and write logic which converts between String (or Number) and the desired Object. 2) maintain a backing map with an unique String (or Number) representation as key and the appropriate Object as value. Both approaches will be descibed here in detail.

Using a Converter is fairly simple. You just need to write some logic how to Convert between String and the desired Object type. Instead of String you can also use any Number type, for example Long, because JSF already has built-in converters for it.

Note the f:converter facet. The value of its converterId attribute must match with one of the converters as definied in the faces-config.xml. The faces-config.xml example will be shown later in this paragraph.

This is how the Foo object type look like. It is actually a random Data Transfer Object (DTO). Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well.

And here is a fake DAO which maintains the Foo DTO's. Take note that the database is simulated by a static backing map, this doesn't need to occur in real life. It should actually be mapped to a database or maybe even configuration files or so. For more information about the DAO pattern, check this article: DAO tutorial - the data layer.

Finally here is the long-awaiting FooConverter which can be used in JSF components. It converts from Foo to String and vice versa. You can attach it to almost any HTML input or output component using the converter attribute or using the f:converter facet.

Take care with runtime exceptions as NullPointerException, ClassCastException and maybe some exceptions which can be thrown by your DAO. Those have to be checked and/or caught and should be thrown as a new ConverterException with the appropriate message which would be shown in the attached h:message(s) tag.

The MyBean and the FooConverter are definied in the faces-config.xml as follows:

You can however also use converter-for-class instead of converter-id so that JSF will always use the specified converter class for the given class. This way you don't need to specify the f:converter in your JSF code.

You can also decide to use a so-called backing Map to maintain String-Object pairs and convert against it in the backing bean logic. The String keys can be used in the view layer (JSF pages) and the Object values can be used in the business and data layers (backing beans and DAO's). You can also use Number-Object pairs however.

The relevant part of the JSF page is almost the same as in the case of using a converter, only the f:converter tag is removed:

And here is the appropriate backing bean MyBean, it has a staticMap of String-Foo pairs. It's your choice how you would load/maintain it, this is just a basic example. Take note that the selectedItem is now a String instead of Foo.

That's it! You can just reuse the Foo DTO, the FooDAO DAO and the faces-config.xml snippet from the Using a Converter paragraph here above. No FooConverter is needed in here and you can also safely remove it from the faces-config.xml.

82 comments:

Thanks for an excellent post.BTW, if you declareCollection SelectItem selectItemsinstead ofList SelectItem selectItemsand try to populate it with HashSets, you'll be facing a 500 error with no sensible exception. That's what I did ;).Can't get the reason why.

@baluscYes, you are right. What was misleading is that in Core JSF 2nd ed. they write that a Collection of SelectItems is supported. Probably I'm getting smth wrong.BTW, as it's very likely that there will be only one converter per class, to use(wish it was possible to post tags here)--converter converter-for-class converter-classconverter--tags inside faces-config. Now you are free from explicitly specifying converter in the code.Now, there's an interesting task I'm beating my brains out on.In a dataTable, how would one represent a List of objects as selectItems?If I get to a solution, I'll post it here.

I normally do not leave comments to blogs. However I feel this blog has save me more time and provided better examples than all the other blogs combined. I just want to say thanks and keep up the great work.

hi balusc, hats off to u .i am relatively new to jsf. i have a question to you.i have a problem with this selectonemenubox.I created a new ListSelectItem .I created a for(enhanced) loop . The list which i referred in for loop was a List of type Company.i add this list with my new ListSelectItems.it gets add to it but when i run jsf there is an error thrown saying"Value binding '#{sprUserHandler.sprUserBean.companyList}' of UISelectItem does not reference an Object of type SelectItem".but if i print List SelectItem by converting it with toString ,its showing me an object of type SelectItem "javax.faces.model.SelectItem@1ed2bbd" . what might the problem

Depending upon the access set for these modules, a user may get to see these modules.

The solution which I have thought of [of course, after reading couple of java sun forums as well] is to dynamically populate a HashMap with keys to these UI/form elements for a module and then, dynamically construct the user access page.

This is working absolutely fine. The problem is when I am trying to capture the submitted values. The HashMap is unable to post the set values of the UI elements.

Hello BalusC. I have implemented a SelectOneMenu based on your coding in this example. However, I'm trying to populate the SelectItems list from a DAO database access. The DAO code gets the list from a Spring JPA access to the database, which works beautifully and gets the data from it when running a JUnit test using AbstractJpaTests.

A different thing is when i try to populate the SelectOneMenu list using the DAO access. In this case I use a similar code like yours; I inject a DAO bean into the JSF controller managed-bean using an initialiser block like your {fillSelectItems} block. However, it throws a NullPointerException, and when debugging, Eclipse debug shows the DAO object variable is null, say, the controller instance variables have not been injected with the DAO bean reference.

¿Do you know why this happens? I've been trying to solve this problem and I'm almost flat on it.

Injecting properties in a bean would only happen after the bean is been constructed and initialized. So you need to fill the menu items right after the injection. You can use lazy loading in the setter of the DAO for that. Or if you're using JSF 1.2, then you can use the @PostConstruct annotation for that, set it on the method which fills the menu items.

hi baluscGood post,I have one question... I case of selectonemenu with java.util.Date objects How the converter is selected? Do I have to mention teh converter like above or do i have to write conveter?

Also how should I populate selectitems with ? is this correct SelectItem(new Date()) ?

Great work! I have it working beautifully. I just have one quick question though.

I have a selectOneRadio that uses a WireType object. I don't want to default that radio but if it's not selected I get a validator error saying 'summary=Validation Error: Value is not valid' I'm guessing this has something to do with a null somewhere. Do you know how I could basically make the converter ignore it if nothing is selected???

The problem is that it doesn't even get to the getAsObject() when I don't select anything. I'm debugging it right now and I see it calling WireType.equals() four times even though I have two options, one time the "this" is null and it compares it against the two options, the next time "this" is one of the options and the obj is null so that method never returns true, after those method calls is when I see it adding a message to the Faces Context. Any ideas?

It's an ibm-jsf implementation. equals() is definetely getting called when I click on a submit button, from what I can tell on the stack trace it's trying to match the selected value to something in the list of SelectItems but no item is selected so it's throwing that validation message. I tried copying and pasting and just changing the names of the values and it's doing the same thing. I also just tried a custom validator that does nothing to see if that would work but it's still going through the HtmlSelectOneRadio .matchValue() method. Maybe I will just have to preselect one of them no matter what....

IBM doesn't have a JSF implementation, but only a component library (the Faces Client Framework, with the 'hx' taglib). Websphere and on ships by default with the early builds of Sun RI 1.0 or 1.1, depending on WS version. In both cases, consider upgrading to the latest 1.1, the 1.1._02 which is available at here. (Re)place the jsf-api.jar and jsf-impl.jar in the WEB-INF/lib of the project.

I just thought it was an IBM implementation because when I created this project in RAD 7 it automatically added jsf-ibm.jar to the WEB-INF/lib so I was just going off of that. I've been googling around and seen other people with this problem so I wonder if a new version will even fix it. I'm pretty limited to upgrading the jar files anyways so I might have to default it no matter what and hope the business doesn't care whether it's preselected or not. I thought a customer validator would work but apparently that doesn't keep it from using the matchValue() method. Thanks for all your help!

The jsf-ibm.jar is the IBM component library. Your problem is related to the actual JSF implementation, which exist of the jsf-api.jar and jsf-impl.jar. If you extract those files and read the manifest file you should see the implementation brand and version. Big chance that it is the Sun RI 1.1 or very maybe 1.1_01. Upgrading to RI 1.1_02 is really worth the effort. The 1.1 is from 2004 and the 1.1_02 is from 2006 and in those 2 years really many bugfixes are been released. I can tell from experience, I've used Websphere 5.x and 6.x for years.

Not sure why it was still breaking, I'm defaulting it now and it's working correctly. Hopefully I don't run into another place where I use a customer converter and don't want it defaulted to something.

Thanks for all your help, also I was looking around your blog and you have tons of great entries!

Thanks for an excellent post. we are also following the same by using Arraylist for selectItems but only the thing is we are using component binding instead of value binding, here i want to set the selected value with the label not with the key so how can I do that.

thanks for your immediate reply, but in my project we use key-lable pair in the business logic the key also plays huge role thats why i can't change my structure but any way I wrote a method to get the f:selectItems and iterating to get the lable and setting that

First of all, again a very interesting read! Informative, to the point and not cluttered with "I'm so smart I use 25 paragraphs to make my point" text :)

Do you have any experience with Seam and using Custom Converters?As far as I know it's more or less the same manner of operation, though the instantiating of components is done through annotations rather then through faces-config.xml

I'm kinda hoping you have some experience in this as well, as I keep getting an error stating that my customConverter cannot be found.

Thanks for the post. It is really helpful. From what I can tell this should work for other tags as well such as h:selectOneRadio. Has anybody tried it and had it work sucessfully? I tried with radio buttons and had no luck. It seems to call the converter and properly convert the objects to strings (ID's), but the correct previously selected option is not checked when I go to the page. If I change only h:selectOneRadio to h:selectOneMenu the correct option is chosen in the list. Any suggestions???

Thanks for the very informative article. One thing that is still unclear to me is how can I create dependent select boxed where change in value in one combo changes the list of values in other combo. It will be very helpful if you provide some information on it.

Second, is there any way I can send you my code so you take a look at it. I've followed every step of your article but I'm still getting the "Conversion Error setting value '' for 'null Converter'" error. I can send you everything you need, view, model, DAO, DTO, faces-config.xml, everything. Just create a directory src/example, drop em in and run em. I'd copy and paste them here but the number of characters exceeds the blog limit.

I can show you some right now though:

whoops, no I can't, not even the Java, think the generics get interpreted as html.

Well, anyway, if there's a way I can send you my files I'd really appreciate it.

Oh, sorry, I'm a bit burned out today, I've got your code verbatim in my JDeveloper 10.1.3.3 playground or sandbox, all the files are in one directory called example and I'm totally psyched, but when I hit the "Add" button to add a row I get the null converter error. That's why I'm wanting to let you look at my files and try to see what the problem is. I email you a war, it's like 2 mb, I'm not at work so I don't know the exact size.

I tried to use the same approach for a h:selectManyCheckbox component.

The converter approach did not work. When I try it with a backing bean having a get/setter pair for List[SelectItem] Spring faces with javax.faces v1.12_08 throws out an error that the value is invalid.

If I change the get/setter pair to SelectItem[] I get the null converter error which you mention in the beginning of your post.

Going with the Map approach works flawlessly however.

When I tried the converter approach, I tried registering the converter with converter-for-class. If murphys law is to be trusted, maybe that one deviation from your example is to blame? However I've seen it work demonstrated by other bloggers..

Thank you for your post. I was trying to use it my application, but faced with null-pointer exception. I use GlassFish and here my bean class with method that calls from converter and causes exception because entity manager is null

How to retrieve the selected value is already explained. How to retrieve the label is straightforward: it's already there in backing bean, just get it by selected value (though I highly question the need to know about the label).

I have a requirement for changing the background of the value in h:SelectOneMenu when the mouse is over the value.I mean the value which the mouse points to should be highLighted i.e if have 10 values in the drop down when my mouse is on the second value in dropdown the value should be highlighted in yellow whereas the others should not be highlighted.I tried using onmouseover attribute but it jut mess up my logic when mouse is over the dropdown all values are highLighted.

I want to remove an item in my selectOneMenu, there is an arraylist in my bean that contains list items. How can I remove selected item via a button.I have already tried to remove selected item from issued array but I could not find the way to reach it.

where document is my managed bean. This give me a Validation Error: Value is not valid. I play around a bit, so I changed the above code to

facilities.add(new SelectItem("", fac.getName()));

and that fix it the error. So making the first argument of the SelectItem an empty string fix the Validation Error, make me thing that I am missing some type of conversion. But since my target object facility in managed bean is type string as well. I am not sure how to fix it. So here is my original code

facilities.add(new SelectItem(fac.getId, fac.getName()));

Do you have any idea how to fix it? I delete the two previous comments because I could not get the html to come out correctly. Srry about that

Balus, i most say, you are the best! sincerely i learned a looot from you. im starting with jsf, and for luck i found your blog, i learned a lot. thanks!!!

now i have a question, i made just like you made this example, but it dosent display any item in my h:selectonemenu, i mean its blank.

im using your DAO and DTO form the another tutorial, i bring the data good, i checked already, the variables selecteditem and selectitem are good, but i dont know why its blank my h:selectonemenu, the list(selectItems) are good populated with the values, but still i dont have any idea, (im very stupid by the way).

Hi BalusC....great blog!....I hope you can help. I'm trying to do something that should be simple. I have a datatable containing a number of lines. Each line contains a selectonemenu that has a list of selectItems (which represent products with an id, name and category).

I basically want it so that when a user changes the product in the select item, the next outputfield in the table displays the category.

For saving on database purposes it worked well; but I could not get it working when I get some object from database on editing purposes, i.e., the drop down does not show the current value being edited, it appears blank.

Hi,Please is there any way to add Filter function to the standard JSF SelectOneMenu. I have a large list of objects to be showed in the SelectOneMenu that is why I prefer using native JSF component rather than using Primefaces one. However I am facing a problem with adding filter function to the component. Can you please help in this issue?

About

Donate

For the ones who want to express their excessive thanks for my work, I used to have an Amazon wishlist with a list of books, but right now I don't have any interesting books on the list anymore (to anyone who've sent books before: thank you very much, I got 6 books in 6 months). You can always donate something so that I can use it for other stuff, such as Nespresso coffee.