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

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

From: Josh Juneau <juneau001_at_gmail.com>
Date: Wed, 15 Apr 2015 06:31:53 -0500

Leonardo,

Thanks for the excellent write-up. I agree that this seems like a bug in
the implementation, and it should be repaired.

Best

Josh Juneau
juneau001_at_gmail.com
http://jj-blogger.blogspot.com
https://www.apress.com/index.php/author/author/view/id/1866


On Tue, Apr 14, 2015 at 8:42 PM, Leonardo Uribe <leonardo.uribe_at_irian.at>
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
>