users@javaserverfaces-spec-public.java.net

[jsr372-experts mirror] Fwd: [jsr372-experts] Re: Improvements for ajax update (related to Handling Focus after Ajax update)

From: Werner Punz <werner.punz_at_gmail.com>
Date: Thu, 16 Apr 2015 08:41:12 +0200

Hi sorry to interrupt here negatively, but

using the protocols attribute directives causes sideffects on the
javascript side for the component authors.

I will give a short writeup what the problem is.

Up until now component authors could rely on the elements being replaced.
Now in case of following
node.addEventListener("event", function...

The event listeners were cleared up, and could be reset via a jsf ajax
event listener.

Now if we introduce the case of replacing the elements in one case and just
replacing some attributes in another case we get the behavior of having
cleared up the registered events just like we used to do and not having
them cleared up in another (from the outside) unpredictable case.
In the worst case the event handlers stack up in the component because they
are registered over and over again on the javascript side.

The solution to this according to what I could find out is to clone and
replace the element just before the attribute is set, which is also not
really that good from a performance perspective and might fail in the
future (not sure if this clearing up of events during node.clone really is
a documented feature).

If this is just wanted to preserve the focus, then I think there are other
ways to do it properly (see my last mail regarding the topic)


Werner




On Wed, Apr 15, 2015 at 2:36 PM, manfred riem <manfred.riem_at_oracle.com>
wrote:

> Hi Leonardo,
>
> Thank you for echoing the same sentiment with regards to Ajax and
> rendering that I tried to explain with regards to f:ajax on the EG call.
>
> Thanks!
>
> Kind regards,
> Manfred Riem
>
>
> On 4/14/15, 8:42 PM, Leonardo Uribe wrote:
>
>> Hi
>>
>> I would like to point out some details on the JSF spec about how the ajax
>> update is being done. This is related to the topic "Handling Focus on
>> Ajax".
>> My intention is answer the question "why JSF update the whole markup and
>> not only the affected attributes" and identify the current flaws on the
>> spec.
>>
>> In my opinion this is a REALLY IMPORTANT FIX, and fortunately the
>> solution can
>> be done with a bit of effort.
>>
>> The problem is all about how a JSF Component is rendered using an ajax
>> request.
>> According to JSF 2.2 spec section 13.4.3 Partial View Rendering, it
>> says something
>> like this:
>>
>> "... The UIViewRoot getRendersChildren and encodeChildren methods must
>> determine
>> if the request is an Ajax request using the FacesContext.isAjaxRequest()
>> method.
>> If PartialViewContext.isAjaxRequest() returns true, then the
>> getRendersChildren
>> method must return true and the encodeChildren method must perform partial
>> rendering using the PartialViewContext.processPartial implementation ..."
>>
>> This is what it says on PartialViewContext.processPartial(...) javadoc:
>>
>> "... Perform lifecycle processing on components during the indicated
>> phaseId.
>> Only those components with identifiers existing in the Collection
>> returned from
>> getExecuteIds() and getRenderIds() will be processed. ..."
>>
>> In few words, PartialViewContext.processPartial(RENDER_RESPONSE_PHASE)
>> method
>> is the one responsible of call encodeXXX methods.
>>
>> The problem can be seen when you take a look at how the default algorithm
>> for
>> PartialViewContext.processPartial(...) really works. I'll describe the
>> steps
>> done by this algorithm by default in MyFaces (only what's relevant of
>> course):
>>
>> - Get PartialResponseWriter
>> - Call writer.startDocument() and set id attribute
>> - If isRenderAll() true then
>> - Call writer.startUpdate()
>> - Call component.encodeAll() for every children of UIViewRoot instance
>> - Call writer.endUpdate()
>> - Otherwise for each component in renderIds:
>> - Call writer.startUpdate()
>> - Call component.encodeAll()
>> - Call writer.endUpdate()
>> - Call writer.endDocument()
>>
>> Now, let's suppose you have a component and you know that the only thing
>> that
>> changes between ajax request is the attributes attached with
>> ValueExpression
>> instances. In
>>
>> <h:inputText value="#{myBean.value}">
>> <f:ajax ...>
>> </h:inputText>
>>
>> HTML Markup
>>
>> <input id="j_id_23" type="text" value="..."/>
>>
>> Only changes "value" attribute, right?.
>>
>> How to do it with the current plumbing? If you take a look at the xsd of
>> web-partialresponse, you can see this is a possible response:
>>
>> <partial-response id="j_id_0">
>> <changes>
>> <attributes id="j_id_23">
>> <attribute name="value" value="new value">
>> </attributes>
>> <update id="otherCompId"><![CDATA[... HTML Markup ...]]></update>
>> </changes>
>> </partial-response>
>>
>> The problem is the default algorithm is not flexible enough, because the
>> component cannot choose the proper command. By default, a call to update
>> the DOM markup is always done and it is always called encodeAll().
>>
>> Third-Party vendors usually override
>> PartialViewContext.processPartial(...)
>> to do some extra processing, but on the way, they override existing
>> behavior
>> and create incompatibilities between libraries overriding the same lines
>> of
>> code.
>>
>> The proposal is add a new method in UIComponent called
>> encodeAjax(FacesContext context, PartialResponseWriter writer) that by
>> default execute something like this:
>>
>> try
>> {
>> writer.startUpdate();
>> this.encodeAll();
>> } finally {
>> writer.endUpdate();
>> }
>>
>> In other words, move the code that writes the "update" section to the
>> component,
>> to let the component choose how and when should be updated. In that way,
>> the component can choose between the valid options:
>>
>> - update
>> - insert DOM node
>> - delete
>> - attributes
>> - eval
>> - extension
>>
>> This change will provide a lot of flexibility that currently we just
>> don't have
>> "out of the box".
>>
>> Let's try something more complex. Suppose this code:
>>
>> <div id="myBigAndComplexPanel">
>> <!-- A big stateless chunk of HTML Markup -->
>> <input id="j_id_23" type="text" value="..."/>
>> <!-- Another big fat stateless chunk of HTML Markup -->
>> </div>
>>
>> And I want to update "myBigAndComplexPanel", but I know that the only
>> thing I
>> really need is update the input value. How?
>>
>> The key point is to know if each component that matters has an id
>> rendered on
>> the DOM markup. If the id is not rendered, we can't update the component
>> individually, because we don't have the reference, and we will be forced
>> to
>> update the whole DOM markup for the panel. Suppose the triggering ajax
>> says this:
>>
>> <f:ajax ... render="myBigAndComplexPanel"/>
>>
>> The algorithm call UIComponent.encodeAjax(...) for the panel. Inside, the
>> component can check if is "stateless" or not, and then invoke
>> UIComponent.encodeAjax for each child component. UILeaf instances (HTML
>> markup)
>> will not process anything, unless they contain embedded EL, but the input
>> component will think the best way to update itself and at the end it will
>> add the<attributes>...</attributes> block. If we can identify when the
>> output of a component has an id (UIComponent.isIdRenderedOnMarkup()) or
>> contains static markup or not (UIComponent.isRenderStatelessMarkup()),
>> this
>> strategy will work. Also something like UIComponent.getUpdatedAttributes()
>> that used the "delta" state or the existence of ValueExpression instances
>> in
>> attributes, to get which ones were changes and build the response.
>>
>> In summary: "why JSF update the whole markup and not only the affected
>> attributes". Because "<update>" solves most of the use cases in core JSF.
>> It can be done, but there are some use cases that are not so simple to do
>> and requires some work. Anyway the fix for "handling focus" is required.
>>
>> regards,
>>
>> Leonardo Uribe
>>
>>