views:

308

answers:

1

Hi,

I would like to know how to use Converters in Java Server Faces similar to Spring collection property editor

Suppose the following model

public class Group {

    private String name;

    List<User> users = new ArrayList<User>();

    // getter's and setter's
}

And equivalent form

<form ...>
    <h1>Group form</h1>
    <label for="name">Enter name</label>
    <input type="text" name="name"/>

    <label for="users">Select users</label>
    <!--value attribute stores userId-->
    <input type="checkbox" value="1" name="users"/> User 1
    <input type="checkbox" value="2" name="users"/> User 2
    <input type="checkbox" value="3" name="users"/> User 3
</form>

If i use Spring to bind users property in Group class, i call

binder.registerCustomEditor(List.class, new CustomCollectionEditor() {
    protected Object convertElement(Object userId)  {
        return new User((Integer) userId);
    }
});

How do i get the same effect when using Java Server Faces ?

regards,

+1  A: 

For that you can implement javax.faces.convert.Converter. Its API is pretty self-explaining: write the getAsString() method accordingly that it returns the String representation of the Object, which can be under each the technical ID such as userId. Then, to get JSF set the right Object during apply request parameters phase, you need to implement getAsObject() that it returns the Object associated with the given String value.

Basically:

public class UserConverter implements Converter {

    private UserDAO userDAO = SomeDAOManager.getUserDAO();

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        return String.valueOf(((User) value).getId());
    }

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        return userDAO.find(Long.valueOf(value));
    }

}

Register it in faces-config.xml as follows:

<converter>
    <converter-for-class>com.example.model.User</converter-for-class>
    <converter-class>com.example.converter.UserConverter</converter-class>
</converter>

That should be it. For more insights you may this or this article useful.

BalusC
Thank you, BalusC (+1). But is it mapped to users property automatically ?
Arthur Ronald F D Garcia
You don't need to worry about that. The conversion is fully transparent. Just define once in `faces-config.xml` and you should be fine.
BalusC
Nice article, your answer is accepted
Arthur Ronald F D Garcia
BalusC, i suppose this Converter is "global". But what should i do to set up a "local" Converter ?
Arthur Ronald F D Garcia
Yes, it will convert every `#{user}` which JSF encounters during render response (for display) or apply request values (to set back as bean property). If you want to use it individually, use `<converter-id>` instead of `<converter-for-class>` and specify the same `<converter-id>` value in the `converterId` attribute of the JSF component. However, I question the value of just displaying `#{user}` in plain text (which would just do `String.valueOf(user)`). A global declaration is much easier and cleaner.
BalusC
You are right, but there are exception to the rule.
Arthur Ronald F D Garcia
Note that this does **not** affect its properties like `#{user.id}`, `#{user.name}` and so on.
BalusC
Is it a good pattern to use a Converter when using inheritance or should i use a PhaseListener ?
Arthur Ronald F D Garcia
Depends on the use case. What kind of inheritance are you talking about? Super/subclasses of the model or the converter? If the first, the converter will only cover the subclasses. If the second, I don't forsee any problems. A PhaseListener is by the way only of value if you actually want to hook on any of the phases and this doesn't seem to be the case.
BalusC
Subclasses of the model
Arthur Ronald F D Garcia