Francisco,

I have found that there is a fundamental problem with how UINamingContainers 
are processed when row indexes are inserted into the ids of 
EditableValueHolders within the container rows. 

Understanding The Problem:
As you may already know, tables have only columns referenced in the page so it 
can handle an almost infinite number of rows. Well, because you have only 
specified an id for a component within the table column the component inserts a 
row index reference in the holders id (via getClientRowKey() in a table) for 
each row. This ensures that each iteration of that row has a unique id. The 
problem with this is that in some cases this breaks automatic setting of bean 
values (if they are value bound to an EditableValueHolder within a row). 
Another issue is that "selectOneChoice" components in particular do not have a 
mechanism in place to ensure that a submitted value that gets set on the 
component is one of the values within the internal "SelectItemList" so 
virtually any value can get set on it. If the submitted value is not in the 
list it adds a blank entry in the drop down menu- what? I have also noticed 
that selectOneChoice component does not handle value conversion like regular 
input text components do. Semi-primitive values such as Integers, Booleans, 
etc. are not converted automatically like they are for regular input text 
components. I had some of the same issues with the "detailStamp" so I created 
an extended table component that allows only one toggled "detailStamp" at a 
time and overrides the "getClientRowKey()" excluding row indexes for components 
within the "detailStamp". This solution works very well, but still does not 
address EditableValueHolders within the rows themselves. It seems to me that we 
need to reevaluate the usage of inserting row indexes into ids!

Possible Solutions:
The first thing I would check is that you have the valuePassThru="true" to 
ensure that its not trying to use the index as the passed value (not sure why 
anyone would want to be restricted to just an index). If want to ensure that 
your value is never set to a value that does not exist in the options you can 
use a value change listener:


        /**
         * Verifies that an editable value holder event components value is a 
valid
         * option. If it is not it sets it back to the old value.
         * 
         * @see #getEditableValueHolderValue(EditableValueHolder)
         * @param event
         */
        public final void verifyValueOption(ValueChangeEvent event) {
                try {
                        if (event.getComponent() instanceof 
EditableValueHolder) {
                                EditableValueHolder valueHolder = 
(EditableValueHolder) event
                                                .getComponent();
                                if (!isValidSelectOption(event.getComponent(), 
event.getNewValue())) {
                                        // invalid option- regress to old value
                                        
valueHolder.setValue(event.getOldValue());
                                        
valueHolder.setSubmittedValue(event.getOldValue());
                                }
                        }
                } catch (Throwable e) {
                        log.warn("Unable to verify select value. " + 
e.getMessage()
                                        + " cause: " + e.getCause());
                }
        }

        /**
         * Determines if the specified value is a valid selection option in the
         * specified select component.
         * 
         * @param component
         * @param value
         * @return is the specified value a valid select option
         */
        public static final boolean isValidSelectOption(UIComponent component,
                        Object value) {
                try {
                        List<SelectItem> items = 
SelectItemSupport.getSelectItems(
                                        component, 
SelectItemSupport.getConverter(component));
                        for (SelectItem item : items) {
                                if (item.getValue() != null && 
item.getValue().equals(value)) {
                                        return true;
                                }
                        }
                } catch (Exception e) {
                        _LOG.warning("Unable to determine if the specified 
value: " + value
                                        + " is valid for the select component: 
" + component);
                }
                return false;
        }



Probably not the most elegant solution, but nonetheless an effective one is to 
manage the submitted row index problems yourself. If nothing else this will 
provide you with a debugging tool to determine if the row index references are 
preventing your values from getting set:
        /**
         * <p>
         * Gets a parameter value from the request scope by an editale value 
holder
         * components id. This method will set/return the holders submitted 
value
         * from the request.
         * </p>
         * <p>
         * The holders client id is checked against a possible matching id 
request
         * parameter. The client id of the holder and the request parameter 
matching
         * the hodler id may have their own row indexing (such is the case with 
a
         * select menu).
         * <ol>
         * <li>The client id against the request parameter</li>
         * <li>The client id w/o row index against the request parameter</li>
         * <li>The client id against the request parameter w/o row index</li>
         * </ol>
         * </p>
         * 
         * @param context
         * @param holder
         * @return the parameter value
         */
        @SuppressWarnings("unchecked")
        public static final Object getRequestParameterByHolderId(
                        FacesContext context, EditableValueHolder holder) {
                if (log.isDebugEnabled())
                        log.debug("getRequestParameterForNamingContainer(" + 
context + ','
                                        + holder + ")");
                if (holder != null && holder instanceof UIComponent) {
                        String clientId = ((UIComponent) 
holder).getClientId(context);
                        String clientIdWithOutRowIds = 
removeRowIdReferences(clientId);
                        String id = ((UIComponent) holder).getId();
                        Map<String, Object> map = context.getExternalContext()
                                        .getRequestParameterMap();
                        if (map != null && map.entrySet() != null) {
                                for (Map.Entry<String, Object> entry : 
map.entrySet()) {
                                        if (entry.getKey().indexOf(id) >= 0) {
                                                if 
(entry.getKey().equalsIgnoreCase(clientId)
                                                                || 
entry.getKey().equalsIgnoreCase(
                                                                                
clientIdWithOutRowIds)
                                                                || 
removeRowIdReferences(
                                                                                
entry.getKey()).equalsIgnoreCase(
                                                                                
clientId)) {
                                                        try {
                                                                
setConvertedAndValidatedValue(context,
                                                                                
                holder, entry.getValue());
                                                        } catch (Exception e) {
                                                                
log.error("Unable to capture the converted "
                                                                                
+ "value for component(" + holder
                                                                                
+ ") value: " + entry.getValue(), e);
                                                        }
                                                        return 
holder.getSubmittedValue();
                                                }
                                        }
                                }
                        }
                }
                return null;
        }

        /**
         * Removes any row id references that may exist within the specified 
client
         * id
         * 
         * @param clientId
         * @return the client id
         */
        public static final String removeRowIdReferences(String clientId) {
                if (log.isDebugEnabled())
                        log.debug("removeRowIdReferences(" + clientId + ')');
                if (clientId != null) {
                        String[] ids = clientId.split(String
                                        
.valueOf(NamingContainer.SEPARATOR_CHAR));
                        for (String id : ids) {
                                try {
                                        Integer.parseInt(id);
                                } catch (Exception e) {
                                        continue;
                                }
                                clientId = 
clientId.replace(NamingContainer.SEPARATOR_CHAR + id
                                                + 
NamingContainer.SEPARATOR_CHAR, String
                                                
.valueOf(NamingContainer.SEPARATOR_CHAR));
                        }
                }
                return clientId;
        }

        /**
         * Invokes the internal converter/validators(s) for an editable value 
holder
         * and returns the converted value
         * 
         * @param context
         * @param holder
         * @param value
         * @return the converted value
         * @throws ValidatorException
         * @throws EvaluationException
         * @throws MethodNotFoundException
         */
        public static final Object setConvertedAndValidatedValue(FacesContext 
context,
                        EditableValueHolder holder, Object value)
                        throws ValidatorException, EvaluationException,
                        MethodNotFoundException {
                if (holder != null) {
                        if (holder.getConverter() != null) {
                                value = 
holder.getConverter().getAsObject(context,
                                                (UIComponent) holder,
                                                value != null ? 
value.toString() : null);
                        }
                        if (holder instanceof UIComponent) {
                                if (holder.getValidators() != null) {
                                        for (Validator v : 
holder.getValidators()) {
                                                v.validate(context, 
(UIComponent) holder, value);
                                        }
                                }
                        }
                        if (holder.getValidator() != null) {
                                holder.getValidator().invoke(context, new 
Object[] { value });
                        }
                        holder.setSubmittedValue(value);
                        return value;
                }
                return null;
        }

-----Original Message-----
From: Francisco Passos [mailto:[EMAIL PROTECTED]
Sent: Monday, April 09, 2007 11:17 AM
To: William Hoover
Subject: RE: Keeping selectOneChoice selection


No, it is in a facet for a panelPage.

In it, I have a panelGroupLayout, then a panelHorizontalLayout and finally
in it the panelPage.

If you feel it's best, I can attach the code or paste it in the mail body.

Either way, if the selectOneChoice were in a table (something I will
surely need), what would the solution be?

Thank you,

Francisco



> Francisco,
>
> Is your selectOneChoice in a table?
>
> -----Original Message-----
> From: Francisco Passos [mailto:[EMAIL PROTECTED]
> Sent: Monday, April 09, 2007 10:55 AM
> To: adffaces-user@incubator.apache.org
> Subject: Keeping selectOneChoice selection
>
>
> Hello there.
>
> I'm new to JSF and Trinidad, so please bear with my simplistic doubts.
>
> I'm struggling to keep a selectOneChoice selection upon a postback using a
> request-scoped bean.
>
> At first I couldn't even maintain the values in the list, but I found that
> placing a h:inputHidden on the page and declaring its value to be
> #{myBean.valueList}, they could be kept. Furthermore I've tested the seme
> using pageFlow and it worked.
>
> However, I cannot keep the selected value in the dropdown list, it just
> resets.
>
> What is the correct way to do this simple task without using session
> beans?
>
> Thank you for your time and attention,
>
> Francisco Passos
>
>
>


Francisco Passos
Opensoft - Soluções Informáticas, Lda
Telemóvel:  +351 91 238 52 76
Escritório: +351 21 380 44 10
Email:      [EMAIL PROTECTED]


Reply via email to