[
https://issues.apache.org/jira/browse/MYFACES-3904?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15194038#comment-15194038
]
Leonardo Uribe commented on MYFACES-3904:
-----------------------------------------
I have been thinking about this issue for a long time, because the behavior
described here shows some inconsistencies that
are a bit difficult to fix, and requires some time to clarify them.
I tried the following lines of code with the reference implementation (RI)
{code:xml}
<h:selectOneRadio value="#{ajaxRadioBean.favColor1}">
<f:selectItem itemValue="Red" itemLabel="Color1 - Red" />
<f:selectItem itemValue="Green" itemLabel="Color1 - Green" />
<f:selectItem itemValue="Blue" itemLabel="Color1 - Blue" />
<f:ajax render="showColor"/>
</h:selectOneRadio>
<br/>
<h:panelGroup layout="block" id="showColor">
<h:outputText value="#{ajaxRadioBean.favColor1}"/>
</h:panelGroup>
{code}
This is the generated markup
{code:html}
<table id="mainForm:j_idt4">
<tr>
<td>
<input type="radio" name="mainForm:j_idt4" id="mainForm:j_idt4:0" value="Red"
onclick="mojarra.ab
(this,event,'valueChange',0,'mainForm:showColor')" /><label
for="mainForm:j_idt4:0"> Color1 - Red</label></td>
<td>
<input type="radio" name="mainForm:j_idt4" id="mainForm:j_idt4:1" value="Green"
onclick="mojarra.ab
(this,event,'valueChange',0,'mainForm:showColor')" /><label
for="mainForm:j_idt4:1"> Color1 - Green</label></td>
<td>
<input type="radio" name="mainForm:j_idt4" id="mainForm:j_idt4:2" value="Blue"
onclick="mojarra.ab
(this,event,'valueChange',0,'mainForm:showColor')" /><label
for="mainForm:j_idt4:2"> Color1 - Blue</label></td>
</tr>
</table>
{code}
This shows the POST parameters sent:
{code}
javax.faces.source: mainForm:j_idt4:0
javax.faces.partial.execute:mainForm:j_idt4 mainForm:j_idt4:0
{code}
One of the first rules for a Renderer that supports client behavior properties
is that if the component contains
one client behavior it must render the id on the "main" DOM node.
The API used to render ClientBehavior properties assume you can pass a
"targetClientId" that by default is the component clientId, to
provide a context for the generated script in the markup. By the rule that
enforces render the id if there is a client behavior, the
code will be consistent.
But with this bug, I have realized that the "targetClientId" is a "sourceId"
that could be a clientId or just a generated/derived id
with no JSF component counterpart. So, we need to align the code to deal with
this concept, that means change h:selectManyCheckbox and
h:selectOneRadio to provide the itemId as the sourceId.
Looking the response in Mojarra (RI), I have notice this implementation extract
the client id from the source id, even if
the value is not sent in the code, which doesn't look good, because that means
the javascript code has this behavior burned in the
code and that breaks encapsulation principle. That means the RI does not help
here and we need to find a solution for us.
The first thing is in this case the POST should be like this:
{code}
javax.faces.source: mainForm:j_idt4:0
javax.faces.partial.execute: mainForm:j_idt4
{code}
javax.faces.partial.execute should always contains clientIds that can be bound
to real UIComponent instances, even if the value in
javax.faces.source can be used as a value for javax.faces.partial.execute even
in the case this value is not set by the ajax request.
The right way to do it is change the implementation of AjaxBehavior and check
when the value in sourceId is a clientId or not and if
it is not a clientId and "execute" is null, calculate the clientId of the
component and pass it as value, so the javascript side can
do the request properly.
Now, each ajax request looks like this:
{code}
jsf.ajax.request('mainForm:j_id_f',event,
{code}
can we just replace the id with "this":
{code}
jsf.ajax.request(this,event,...
{code}
The answer is yes, we can, but two conditions must be full fit:
1. The id must be rendered, otherwise the clientId will not be sent in the ajax
request.
2. The Behavior script must be rendered as a property of the tag that has the
clientId or the UIComponent instance.
The first point is given, so the second must be checked. That also means we
need to adjust our implementation to set sourceId as null
by default, instead provide the clientId. The only exceptions are
h:selectManyCheckbox and h:selectOneRadio, so it is safe to change
the code.
I have applied a patch for 2.2.x branch (trunk), following the previous
concepts. The tests shows that it works well and it also
provides a benefit, because it reduce the ammount of javascript code written by
f:ajax tag.
I have to say this is a risky patch, but it has enough justification to be
applied.
> jsf.util.Chain() is rendered with wrong event source
> ----------------------------------------------------
>
> Key: MYFACES-3904
> URL: https://issues.apache.org/jira/browse/MYFACES-3904
> Project: MyFaces Core
> Issue Type: Bug
> Affects Versions: 2.2.0
> Reporter: Sven Linstaedt
> Assignee: Leonardo Uribe
> Fix For: 2.2.10
>
> Attachments: MYFACES-3904-1.patch
>
>
> When applying multiple behaviors to an component, they are rendered to be
> executed in chain by using jsf.util.chain(). So far so good.
> When applying a behavior to a component, that renders multiple html input
> elements like h:selectManyCheckbox or h:selectOneRadio, the javascript is
> generated for every html elements, from which everyone gets it's own id
> rendered by appending a ongoing number to the clientId of the component. In
> the myfaces renderer code these Ids are called itemId.
> The problem is, when you apply multiple behaviors to components, which render
> multiple html input elements: in this case all behaviors are wrapped in a
> chained call, which unfortunately receives not the itemId of the currently
> rendered html element as the source parameter, but the clientId of the
> component. According to the spec, the source parameter should contain "the
> DOM element that triggered this Ajax request, or an id string of the element
> to use as the triggering element."
> Because the event handler is bound to the html input element and not to it's
> parent html "wrapping" element used for layouting, the wrong event source is
> rendered in my option, which leads to the following side effect: Behavior
> scripts, that reference the current event producing html element via "this"
> will receive a different calling context ("this), if chain together with
> multiple behaviors than, if they would as single behavior, leading to all
> behavior scripts referencing "this" to run in the wrong context and often
> doing nothing meaningful at all.
> E.g.: chained invocation of
> {code}
> <input id="form:shipmentDateCriteriaOption:0" type="radio"
> name="form:shipmentDateCriteriaOption" value="NEXT_WEEK"
> onchange="$(this).val('')">
> {code}
> and
> {code}
> <input id="form:shipmentDateCriteriaOption:0" type="radio"
> name="form:shipmentDateCriteriaOption" value="NEXT_WEEK"
> onchange="jsf.ajax.request('form:shipmentDateCriteriaOption',event,{'javax.faces.behavior.event':'valueChange'})">
> {code}
> is rendered as:
> {code}
> <input id="form:shipmentDateCriteriaOption:0" type="radio"
> name="form:shipmentDateCriteriaOption" value="NEXT_WEEK"
> onchange="jsf.util.chain(document.getElementById('form:shipmentDateCriteriaOption'),
> event,'$(this).val(\'\')',
> 'jsf.ajax.request(\'form:shipmentDateCriteriaOption\',event,{\'javax.faces.behavior.event\':\'valueChange\'})');">
> {code}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)