-
1. Re: Value binding for selectBooleanCheckbox
hbender Jun 14, 2010 8:04 AM (in response to javapool)Did you try to use a Converter?
Heri -
2. Re: Value binding for selectBooleanCheckbox
javapool Jun 14, 2010 3:28 PM (in response to javapool)Thanks for the response. No I did not use any Converter. I did try that but, I am not sure how to use the Converter to bind the selectBooleanCheckbox values.
-
3. Re: Value binding for selectBooleanCheckbox
hbender Jun 15, 2010 4:06 AM (in response to javapool)You implement the javax.faces.convert.Converter interface. There you must find a way how to convert the involved object instance into a displayable String (easy) (during rendering phase) and a way how to convert the String back into an appropriate object instance (in the restore phase) The latter is often not that easy. Maybe you have to cache somewhere the used Object-String mapping which is complicated by the fact that the converter instance is not the same in rendering and restore phase. I solved it by three cascading static Hashmaps where the involved Class, the ID of the faces component and the current object instance mapped to the desired string representation is involved. I used this construct in a rich:pickList. But it should work the same in a selectBooleanCheckbox since the converter stuff is basic JSF.
The converter class is added to the system by extending faces-config.xml:
<converter>
<converter-id>myConverterName</converter-id>
<converter-class>my.converter.Class</converter-class>
</converter>
and referenced in the JSF component:
<h:selectBooleanCheckbox
converter="myConverterName"
...
Heri -
4. Re: Value binding for selectBooleanCheckbox
javapool Jun 21, 2010 1:42 PM (in response to javapool)Thanks again for keep helping me. I did get all converter implementations like the following
<!-- <converter>
<converter-id>myConverterName</converter-id>
<converter-class>my.converter.Class</converter-class>
</converter> -->
So i know each time the page get rendered, method getAsObject or getAsString was invoked to fetch value from the bean. What I am struggling to understand here is that what is the method that gets triggered after I change the value of selectBooleanCheckbox.
I am newbee to JSF and not sure about how to implement the following logic from your suggestion.
=============
I solved it by three cascading static Hashmaps where the involved Class, the ID of the faces component and the current object instance mapped to the desired string representation is involved. I used this construct in a rich:pickList. But it should work the same in a selectBooleanCheckbox since the converter stuff is basic JSF.
============= -
5. Re: Value binding for selectBooleanCheckbox
hbender Jun 22, 2010 4:17 AM (in response to javapool)I'm not an expert neither, I just experimient around with JSF and Seam. The implementation of the above suggestion is pure jave and has nothing to do with JSF. Below you find my code attached. The entity I used is of my type Permission. Although it is a DB entity it could be an ordinary POJO. The Converter is generic in the sense that it can be used for more than one entity class, and more than one UIComponent. The components and the involved entity classes could even be in a m:n relationship. But it is not absolutely generic because you have to initialize the ID_CLASS_MAP and for each involved entity a format method (see formatPermissionDisplayLabel).
If I had time I would make this code real generic by supplying the initial values through static setter/adder methods, and provide the format-methods by an interface (or just by using the standard toString() method). Maybe you could achieve this, and a seam guru could review it, especially my remarks in the class comment, and the static cache (LABEL_OBJECT_MAP} should be reworked in order to have a session scope (would involve a little more insights into JSF and Seam, which I do not have at the moment).Heri
import java.util.HashMap; import java.util.Map; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import ch.ergonomics.ppuk.entity.Permission; /** * Converter, der Entities in Strings und zurück übersetzt. * Wird z.B. in einer RichFaces Combo-Box gebraucht, um die UserEntity * als String (Namen) darzustellen. Wenn der Request vom Client wieder * zurückkommt (ApplyRequestValues-Phase), dann muss der String wieder in * eine Entity-Klasse übersetzt werden. * <p> * Ich weiss nicht, ob die gewählte Implementierung (über die Maps) * das gelbe vom Ei ist (threadsafe, über mehrere parallele Sessions, * wenn Users gelöscht werden, etc.). Es hat aber den Vorteil, dass * der dargestellte String in der GUI nicht auch noch die ID enthält, * nur um beim zurückkehren anhand dieser ID wieder einen DB-Lookup * zu machen. * * * @author bender */ public class EntityConverter implements Converter { /** Logger for this class */ private static final Log myLog = LogFactory.getLog( EntityConverter.class ); public static final String ROLE_EDIT_PICK_LIST_ID = "roleEditPickList"; /** This map is statically initialized by the UIComponent-ID (ID attribute in JSF) and the * class which is espected in getAsString() */ private static Map<String,Class<?>> ID_CLASS_MAP = new HashMap<String,Class<?>>(); static { ID_CLASS_MAP.put( ROLE_EDIT_PICK_LIST_ID, Permission.class ); } /** * This map holds a submap for each possible Entity class. The submap maps the generated string (getAsString() to * the Entity instance. * <p> * This implementation assumes that always first getAsString() is called, where the submap is populated, and afterwards * the getAsObject, where the corresponding Entity instance can be retrieved by the received String */ // TODO: This map has to be static because the Converter instance is not the same in the "Render Response" phase (getAsString) // and the "Apply Request Values" phase (getAsObject). Maybe experiment around with the additional interface StateHolder (see // javadoc of javax.faces.convert.Converter private static Map<Class<?>,Map<String,Object>> LABEL_OBJECT_MAP = new HashMap<Class<?>,Map<String,Object>>(); /** * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String) */ public Object getAsObject( FacesContext aArg0, UIComponent aArg1, String aArg2 ) { myLog.trace( "in EntityConverter.getAsObject(): FacesContext: " + aArg0 + "; UIComponent: " + aArg1 + "; String: " + aArg2 ); if ( aArg2.length() == 0 ) { myLog.debug( "getAsObject(): received string is empty. Returning a null object" ); return null; } // if aArg2.length() == 0 Map<String, Object> map = retrieveLabelObjectMap( aArg1, null ); // throws if not found Object result = map.get( aArg2 ); if ( result == null ) { throw new AssertionError( "the given String \"" + aArg2 + "\" was not rendered by this getAsString() method!" ); } // if result == null return result; } /** * @see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object) */ public String getAsString( FacesContext aArg0, UIComponent aArg1, Object aArg2 ) { myLog.trace( "in EntityConverter.getAsString(). FacesContext: " + aArg0 + "; UIComponent: " + aArg1 + "; Object: " + aArg2 ); if ( aArg2 == null ) { myLog.debug( "getAsString(): aArg2 is null. Returning null" ); return null; } // if aArg2 == null if ( ( aArg2 instanceof String ) && ( ( ( String ) aArg2 ).isEmpty() ) ) { myLog.debug( "getAsString(): aArg2 is an empty string. Returning an empty string" ); return ""; } // if aArg2 instanceof String ) Map<String, Object> map = retrieveLabelObjectMap( aArg1, aArg2.getClass() ); // creates one if not found String label; if ( aArg2 instanceof Permission ) { // TODO: Our rendering needs could be covered by an Interface "LabelRendered" with method formatDisplayLabel(). label = formatPermissionDisplayLabel( ( Permission ) aArg2 ); } else { myLog.warn( "unhandled class: " + aArg2.getClass().getName() ); label = aArg2.toString(); } // if..else aArg2 instanceof UserEntity map.put( label, aArg2 ); return label; } private Map<String, Object> retrieveLabelObjectMap( UIComponent aArg1, Class<?> aReceivedClass ) throws AssertionError { String id = aArg1.getId(); if ( id == null ) { throw new AssertionError( "the given UIComponent has no ID!" ); } // if id == null Class<?> clazz = ID_CLASS_MAP.get( id ); if ( clazz == null ) { throw new AssertionError( "unhandled UIComponent-ID: " + id ); } // if id == null Map<String,Object> map = LABEL_OBJECT_MAP.get( clazz ); if ( aReceivedClass != null ) { // call comes from getAsString(). Check the correct class: if ( !( clazz.isAssignableFrom( aReceivedClass ) ) ) { throw new AssertionError( "Configured Class (" + clazz.getName() + ") for UIComponent-ID " + id + " is not assignment compatible with received class: " + aReceivedClass.getName() ); } // if !( clazz.isAssignableFrom( aReceivedClass ) ) // if not yet constructed, construct the map now: if ( map == null ) { map = new HashMap<String,Object>(); LABEL_OBJECT_MAP.put( clazz, map ); } } if ( map == null ) { // can only happen on calls from getAsObject() throw new IllegalStateException( "No LabelObjectMap found for class " + clazz.getName() ); } // if map == null && aCreate return map; } private String formatPermissionDisplayLabel( Permission aPermission ) { return aPermission.getName(); } }