Saturday, August 29, 2009

richfaces pickList - validation error

A common error when using pickList is a "Value is not valid" error.

One reason for this is that the selectItem list does not contain one of the selected values. This happens often when you try to created a pre-selected list.

For example, suppose your selectItems are: A, B, C, D, E
And A & B are already selected.

This means that when the pickList is shows, you want A & B to appear in the right side, and you want C, D, E to appear on the left side.

What you need to do is put all five - A, B, C, D & E in the selectItems list (left side list).
Then in the list which will hold the selected items (right side list - the one that you will bind to the value attribute of the picklist), you need to put the select Item value of A & B.

richfaces will automatically show A & B on the right and C, D, E on the left.

However, if you put A & B in the right side list, and pnly C, D, E in the left side list - you will get a value is not valid error!


Secondly, remember that if your value is a non-String object, then you need to override the "equals" method in your Object class.

richfaces pickList - converters

Here are a few things to remember when using richfaces pickList:

1. If the value of your selectItem is a non-String object type, then you need to write a converter. You can have a property on your backing bean to hold an instance of the converter, and you can point the pickList converter to use it.

Like below:

1. -------------- Converter ----------------------------

package org.fastkangaroo.quasimodo.converters;

import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

import org.fastkangaroo.quasimodo.entities.UserProfile;

public class UserProfileConverter implements Converter {
private Map choiceMap;

public Object getAsObject(FacesContext arg0, UIComponent arg1, String label) {
return choiceMap.get(label);
}

public String getAsString(FacesContext arg0, UIComponent arg1, Object obj) {
UserProfile userProfile = (UserProfile) obj;
String label = userProfile.getFirstName() + " " + userProfile.getLastName() + " " + userProfile.getMiddleName();
return label;
}

/**
* @param choiceMap the choiceMap to set
*/
public void setChoiceMap(Map choiceMap) {
this.choiceMap = choiceMap;
}

}


2. ------------------ Beacking Bean --------------------------

public class ProjectBean {

...
private UserProfileConverter userProfileConverter;

...

+ getter/setter
}




3. ------------------- XHTML ------------------

<rich:pickList id="memberPickList"
value="#{projectBean.selectedMembers}"
converter="#{projectBean.userProfileConverter}"
validator="#{projectBean.projectMembersValidator}" >
<f:selectItems value="#{projectBean.memberSelection}" />
</rich:pickList>

richfaces DataTable - getting selected row

Unfortunately, a simple thing like getting which row in the rich:DataTable was selected is not so simple!

For excellent examples, see this page:
http://balusc.blogspot.com/2006/06/using-datatables.html

And, in addition, remember this:

1. Binding the datatable in your xhtml to a backing bean datatable (using the 'binding' attribute) will work only if the backing bean is in request scope. If it is in session scope, you will get a duplicate Id error when you refresh or revisit the page.

2. If you put a commandButton (or commandLink) on each row - it will work only if the action handler backing bean is in session scope. If it is in request scope, the action handler will not be called.

So, these two things are in contradiction to each other. The solution, if you are in this situation, is to have two backing beans - one for binding the datatable and another for handling the commandButton!

Now, if the reason you are binding the datatable in the first place is to use dataTable.getRowData() to identify the row - so that you can get to the row object, then you are in trouble. Because the action handler will go to a different backing bean!

The solution is to use f:setPropertyActionListener to pass the selected row object to the commandButton, as below:
<a4j:commandLink value="#{project.name}" action="#{projectBean.view}">
<f:setPropertyActionListener target="#{projectBean.project}" value="#{project}" />
</a4j:commandLink>

The backing bean, in this case projectBean should have a property called 'project' with getters and setters.