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

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

From: Bauke Scholtz <balusc_at_gmail.com>
Date: Tue, 21 Jun 2016 21:25:42 +0200

Hi,

Have you already looked at BabbageFaces?

Blog:
http://www.beyondjava.net/blog/introducing-babbagefaces-efficient-ajax-dirt-cheap
Demo: https://www.angularfaces.net/babbageFaces
Github: https://github.com/stephanrauh/BabbageFaces

Cheers, B


On Thu, Apr 16, 2015 at 7:51 AM, Leonardo Uribe <leonardo.uribe_at_irian.at>
wrote:

> Hi
>
> KM>> Thanks for the detailed explanation; I like what you're
> KM>> proposing for partial updates; you have provided the
> KM>> implementation details of the JSON rendering feature
> KM>> I mentioned at JavaOne, except without JSON, of
> KM>> course (which is fine)
>
> Yes, that's the idea. Just provide some more concrete ideas
> about what to do and how.
>
> I just wanted to mention there are two kinds of ajax request:
>
> - Requests that update a single component with no children.
> - Requests that update an specified region in the page (for example
> render="@form").
>
> I think in the first case, just include UIComponent.encodeAjax(...) is
> more than enough. It is a good idea to send JSON instead xml to make
> it less verbose.
>
> The second case is a bit hard to get it out. The most difficult case
> is what happen with facelet dynamic tags and programmatically added
> components (c:if, c:forEach, c:choose, ui:include src="#{...}",
> ui:decorate template="#{...}"). For example:
>
> <h:panelGroup id="panelToUpdate">
> <h:inputText value="#{myBean.input"/>
> <c:if test=#{...}>
> <h:outputText value="#{myBean.value"}/>
> </c:if>
> </h:panelGroup>
>
> I think a solution to the problem is just put a mark on the parent
> component that holds the dynamic section, so the algorithm can be
> aware that the content of the component is dynamically generated and
> should be updated fully. We have already done things like that in
> MyFaces 2.0
> (org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE
> mode uses this as a way to identify which sections need to be saved
> fully). Programmatically added components contains special markers
> that are implementation specific. So, it could be good to add
> UIComponent.markRenderDynamic(), UIComponent.isRenderDynamic()
>
> In few words, a good algorithm should be able to:
>
> - Identify when a component renders a "dynamic markup" and doesn't
> have an associated id to the dom tree, or
> - Identify if it contains some facelets dynamic components or
> programmatically added components.
>
> One good use case where this feature could be helpful is <f:ajax
> render="@form"/>. Instead send the whole form markup, the algorithm
> will select recursively what really needs to be updated, saving render
> time and bandwidth.
>
> KM>> I think there should also be a rendering hook for the
> KM>> component in JavaScript so it can decide how to
> KM>> render itself
>
> It has sense, if you can send something else on the response that is
> not html markup, the component should be able to process it on the
> client, and JSF should be able to help fill the gap. It is in fact a
> mini-lifecycle:
>
> - Send request.
> - Receive response.
> - Decode response.
> - Update DOM tree
> - Update ViewState
> - Invoke component handler passing the response (JSON or whatever).
>
> I have always thought jsf.ajax.request(...) onevent is very rigid,
> because you can't post-process the incoming response (js function
> callback).
>
> regards,
>
> Leonardo Uribe
>
> 2015-04-15 17:07 GMT-05:00 Leonardo Uribe <leonardo.uribe_at_irian.at>:
> > Hi Kito
> >
> > KM>> I'm not 100% convinced your solution solves the focusing issue,
> > though. How would it handle the following panel:
> >
> > Please note I'm trying not to mix this with the "focus" problem. We
> > need a solution for that problem anyway, because the algorithm
> > proposed here will not solve the main issue (sometimes there is no
> > other choice than a full update).
> >
> > In that case the panel should be updated fully, because there is no
> > "counterpart" in the DOM tree for the embedded html markup.
> >
> > Let's suppose that we have included UIComponent.encodeAjax(...) (and
> > probably Renderer.encodeAjax(...)). The general algorithm should be
> > something like this:
> >
> > - Call UIComponent.isRenderStatelessMarkup() for the panel.
> > - If is false, do a full update as usual.
> > - If is true, check on each child for isRenderStatelessMarkup().
> > - For each "direct" children that does not return stateless
> > markup, check UIComponent.isIdRenderedOnMarkup(). If every child that
> > does not render stateless markup has an id rendered, invoke
> > UIComponent.encodeAjax(...) on each child (even if is stateless
> > markup). The result is the union of those invocations is equivalent to
> > a full update.
> > - Otherwise update fully.
> >
> > In the panel case, the UILeaf instance holding the EL expression
> > #{myBean.value} will return isRenderStatelessMarkup() false and
> > isIdRenderedOnMarkup() false, so the panel will be updated fully.
> >
> > The nice thing is if the component is something complex like a
> > datatable, you can override encodeAjax(...) to do the logic in the
> > same way the renderer does.
> >
> > The key point is that with the inclusion of
> > UIComponent.encodeAjax(...), we gain more flexibility in the way how
> > the view is updated after an ajax operation. I'm proposing here to do
> > that and go further, adding a logic that could allow to check which
> > pieces of information really needs to be updated.
> >
> > I have not checked each detail of the algorithm, but I have the
> > feeling this is going to work. My only concern is the time spent
> > iterating the component tree, so we need to adapt the algorithm to
> > ensure linear complexity (maybe a two-step algorithm seems better).
> >
> > regards,
> >
> > Leonardo Uribe
> >
> > 2015-04-15 9:35 GMT-05:00 Kito Mann <kito.mann_at_virtua.com>:
> >> 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://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
> >>
> >>
>