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

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

From: Kito Mann <kito.mann_at_virtua.com>
Date: Wed, 15 Apr 2015 10:35:46 -0400

Hello Leonardo,

Thanks for the detailed explanation; I like what you're proposing for
partial updates; you have provided the implementation details of the JSON
rendering feature I mentioned at JavaOne, except without JSON, of course
(which is fine). In addition, I think there should also be a rendering hook
for the component in JavaScript so it can decide how to render itself (this
way, the component can decide the attributes refer to something other than
specific DOM element attributes).

I'm not 100% convinced your solution solves the focusing issue, though. How
would it handle the following panel:

<h:panelGroup id="panelToUpdate">
  <h:inputText value="#{myBean.input"/>
  <h:outputText value="#{myBean.value"}/>
#{myBean.value}
</h:panelGroup>


___

Kito D. Mann | @kito99 | Author, JSF in Action
Virtua, Inc. | http://www.virtua.com | JSF/Java EE training and consulting
http://www.JSFCentral.com | @jsfcentral
+1 203-998-0403

* Listen to the Enterprise Java Newscast: *http://
<http://blogs.jsfcentral.com/JSFNewscast/>enterprisejavanews.com
<http://ww.enterprisejavanews.com>*
* JSFCentral Interviews Podcast:
http://www.jsfcentral.com/resources/jsfcentralpodcasts/
* Sign up for the JSFCentral Newsletter: http://oi.vresp.com/?fid=ac048d0e17

On Tue, Apr 14, 2015 at 9: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
>