jsr372-experts@javaserverfaces-spec-public.java.net

[jsr372-experts] Re: [jsr378-experts] h:selectOneRadio "group" implemented as spec says does not work

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Thu, 4 May 2017 13:48:31 -0500

Hi

I see. Yes, in that sense it is ok to use it.

In the component there are two use cases:

1. h:selectOneRadio is inside a dataTable or repeat component like this:

<h:dataTable id="table" value="#{selectOneGroupBean.myValues}" var="val">
    <h:column>
        <h:selectOneRadio id="radio" group="someGroup"
            value="#{selectOneGroupBean.selectedValue}">
            <f:selectItem itemValue="#{val}" itemLabel="#{val}"/>
        </h:selectOneRadio>
    </h:column>
</h:dataTable>

2. There are more than one h:selectOneRadio, but there is one that holds
the select list. Each one has an id ending with an index that indicates
the value that will be rendered in the current component. For example:

<h:selectOneRadio id="radio0" group="someGroup"
value="#{selectOneGroupBean.selectedValue}">
    <f:selectItems value="#{selectOneGroupBean.myValues}" />
</h:selectOneRadio>
<h:selectOneRadio id="radio1" group="someGroup" />
<h:selectOneRadio id="radio2" group="someGroup" />


I think the best trick to make it work is take a look at "value"
ValueExpression. When this VE is not present, the component is a
placeholder,
when it is present, the component can hold a submitted value.

On h:selectOneRadio renderer decode() if the component has "group" property
set, retrieve the value and if the value starts with the component clientId,
the current component will be the "source component". Now, the algorithm
should apply a visit tree call starting from the closest UIForm. When it
founds a component than belongs to the group there are two cases:

- If the source component has a "value" ValueExpression, call
setSubmittedValue(...) and if the target component is the source component
pass the submitted value, otherwise call resetValue().
- If the source component does not have a "value" ValueExpression, find
the first component that belongs to the groupd and with a "value"
ValueExpression and set the value there, otherwise call resetValue().

This algorithm looks more consistent because:

- It ensures only one visit tree call per group.
- It ensures values from previous requests will be cleared.

Then, on render phase, the Renderer will detect the group attribute and if
the attribute is set, it will get the value to use using the same rules
used in decode() phase. This means only a visit tree call is necessary when
option 2 is used.

I think the algorithm proposed respect the spirit of the spec, and I have
already tested it, so I think it should be the way at least for MyFaces.

regards,

Leonardo Uribe




2017-05-04 1:41 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:

> Hi,
>
> Client ID prepended to value is in order to check if the component itself
> should process the submitted value or ignore it and leave it to another
> component of same group (because they all share the same client/parameter
> name). This is by the way intentionally left unspecified in spec and thus
> an implementation detail, although I do for now not see better/nicer ways
> to achieve the desired behavior via the JSF component API.
>
> Cheers, B
>
>
>
> On Wed, May 3, 2017 at 6:51 PM, Leonardo Uribe <leonardo.uribe_at_irian.at>
> wrote:
>
>> Hi
>>
>> Unfortunately sometimes you don't see the problems until you try to
>> implement what the spec says.
>>
>> I agree with you about the strategy to implement (move decoded value to
>> the first component of the group), but I need to check this part fully to
>> see the spec changes. I think the javadoc/api will not change, but the
>> implementation needs to be clarified or refactored. I still don't
>> understand why append the clientId with the value, but in principle it is
>> not a problem.
>>
>> regards,
>>
>> Leonardo Uribe
>>
>>
>> 2017-05-03 2:29 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>
>>> Hi,
>>>
>>> You're right about the oversight in the spec on this. In fact, I'm
>>> surprised the spec changes was not reviewed properly.
>>>
>>> Basically, when components of the group are collected, and the component
>>> of interest doesn't have "value" attribute set, then it will delegate to
>>> the "value" attribute of the first component of the group, if any. I'm as
>>> of now only unsure where exactly in the spec this had to be specified.
>>>
>>> Cheers, B
>>>
>>> On Wed, May 3, 2017 at 8:27 AM, Leonardo Uribe <leonardo.uribe_at_irian.at>
>>> wrote:
>>>
>>>> Hi
>>>>
>>>> I have been trying to implement h:selectOneRadio "group" behavior in
>>>> MyFaces from scratch and I have found some problems.
>>>>
>>>> The problem starts in this example:
>>>>
>>>> <h:form id="mainForm">
>>>> <h:selectOneRadio id="radio0" group="someGroup"
>>>> value="#{selectOneGroupBean.selectedValue}">
>>>> <f:selectItems value="#{selectOneGroupBean.myValues}" />
>>>> </h:selectOneRadio>
>>>>
>>>> <h:selectOneRadio id="radio1" group="someGroup" />
>>>> <h:selectOneRadio id="radio2" group="someGroup" />
>>>>
>>>> <br/>
>>>> <h:outputText value="Selected Value:
>>>> #{selectOneGroupBean.selectedValue}"/>
>>>> <br/>
>>>> <h:commandButton value="Submit" actionListener="#{selectOneGro
>>>> upBean.update}"/>
>>>> </h:form>
>>>>
>>>> The rendered markup by Mojarra (RI) is this:
>>>>
>>>>
>>>> <input type="radio" name="mainForm:someGroup" id="mainForm:radio0"
>>>> value="mainForm:radio0:A A" />
>>>> <label for="mainForm:radio0"> A A</label>
>>>> <input type="radio" name="mainForm:someGroup" id="mainForm:radio1"
>>>> value="mainForm:radio1:B-B" />
>>>> <label for="mainForm:radio1"> B-B</label>
>>>> <input type="radio" name="mainForm:someGroup" id="mainForm:radio2"
>>>> value="mainForm:radio2:C_C" />
>>>> <label for="mainForm:radio2"> C_C</label>
>>>>
>>>> Take a look at the "value" attribute. It appends the clientId and the
>>>> value. This means when decode() happens, only the component with the right
>>>> clientId takes the value. But please note only radio0 has the EL expression
>>>> pointing to the model. Which means at some point setSubmittedValue(...) is
>>>> called, but if is not in decode(), when?
>>>>
>>>> There is an inconsistency in decode(). In fact, I'm suprised the part
>>>> that talks about that in the spec was not updated properly.
>>>>
>>>> There is also another problem caused by submitted values not processed
>>>> by the validation step. The spec talks about skip processValidation(...)
>>>> with some conditions, to avoid validation step over components that does
>>>> not have the validators and the EL "value" expression. But if there is an
>>>> stale "submittedValue" in a component, this value is saved/restored by the
>>>> state saving algorithm, causing a confusion with further requests. Ok, in
>>>> decode() you can call setSubmittedValue(null) or something like that to
>>>> make them dissapear, but if there is an ajax requests, you cannot count on
>>>> that, and the problem will appear.
>>>>
>>>> In my opinion this part is unstable. I guess I could find more holes in
>>>> it, but by some reason the implementation in Mojarra (2.3.0) works, at
>>>> least for the basic example.
>>>>
>>>> I'm still working on a implementation, but from this point I have to
>>>> create an alternate implementation that probably will not do what the spec
>>>> says, specially in the part related to UISelectOne.processValidators().
>>>>
>>>> regards,
>>>>
>>>> Leonardo Uribe
>>>>
>>>>
>>>
>>
>