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

[jsr372-experts] Re: [JAVASERVERFACES_SPEC_PUBLIC-1423] Dynamic resource loading in ajax requests

From: Bauke Scholtz <balusc_at_gmail.com>
Date: Tue, 21 Jun 2016 14:51:24 +0200

Hi,

OmniFaces has 3 solutions to dynamically include a (script) resource:

1: Ajax#load() (just write script straight to <eval>)
2: Components#addScriptResourceToHead()
3: Components#addScriptResourceToBody()

Which one to use depends on the moment (which isn't yet refactored into a
single utility method, this is TBD). When the request is an ajax request
with partial rendering (i.e. ajax with NO @all), then use #1. Else when the
current phase is NOT render response, or the view is in BUILDING state,
then use #2. Else use #3.

This is implemented for the unload script associated with OmniFaces
@ViewScoped and this works quite well.

Also, to avoid duplicates and/or unnecessary re-rendering, all so far
rendered resources must be remembered in JSF state. AFAIC this is currently
not the case.

Cheers, B


On Tue, Jun 21, 2016 at 2:37 PM, Leonardo Uribe <leonardo.uribe_at_irian.at>
wrote:

> Hi
>
> I have seen this issue has been opened in the spec issue tracker
>
> https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1423
> Dynamic resource loading in ajax requests
>
> I would like to contribute with some thoughts about the topic, since in
> MyFaces there
> is a solution that is not perfect (if PartialViewContext is overriden, the
> code will
> not work, so some third party libraries will not be able to use it), but
> it helps to
> identify the missing points in the spec, and specify it faster.
>
> This issue could be also relevant for portlets.
>
> The issue has been reported long time ago in:
>
> - https://issues.apache.org/jira/browse/MYFACES-3106
> Resources not loaded when using a dynamic ui:inlclude and rendered via ajax
>
> - https://issues.apache.org/jira/browse/MYFACES-3367
> Detect when to update head or body target when content has been updated
> dynamically
>
> - https://issues.apache.org/jira/browse/MYFACES-3659
> Conditional include of scripts and stylesheets
>
> The first thing to understand the problem is identify the possible use
> cases we have:
>
> - c:if, ui:include src="#{...}" or facelet tag that dynamically update the
> tree.
>
> For example:
>
> ...
> <f:ajax event="onclick" render="box"/>
> ...
>
> <div jsf:id="box">
> <c:if test="#{...}">
> <h:outputScript target="head" library="js" name="help.js" />
>
> ...
> </c:if>
> </div>
>
> Here we have a case when the components inside the c:if requires some
> javascript file,
> but the update comes from an ajax request. The algorithm just somehow put
> the
> h:outputScript on the head of the document, but the ajax does not trigger
> the head
> refresh, so the component was added to the tree, but the javascript file
> was not loaded
> and we finally have a broken panel.
>
> There are variants of the same example with h:outputStylesheet, or
> ui:include but the
> reason is the same.
>
> - Dynamic but the resource is added by a @ResourceDependency annotation.
>
> This is similar to the previous case, but the component is added
> indirectly when
> Application.createComponent(...) is executed.
>
> The important thing to keep in mind is there are 3 possible targets:
> "head", "body" and
> "form". But the only thing we can do with the current spec is just force a
> full update
> of each of these targets.
>
> So, in MyFaces there is a web config param called
> org.apache.myfaces.STRICT_JSF_2_REFRESH_TARGET_AJAX that when it is
> activated, and
> the previous situation happens, it just enable a flag so the target is
> updated with
> the ajax request. If the "head" needs to be updated, the whole "head" and
> the updated
> content is sent and so on.
>
> The processing is done on process partial render time, after the ajax
> request is
> rendered but before write the state token. Please note if the target is
> rendered before
> this code, the algorithm should be able to detect the condition and do not
> duplicate
> the response.
>
> The solution is not perfect because it force render the whole target, when
> we only
> need to render the html fragment of the added component (script/stylesheet/
> other js code). But there is no way to do it with the current API, because
> <head>
> or <body> could not have an id and without id, you can't insert.
> PartialResponseWriter
> contains these methods:
>
> startInsertBefore(String targetId)
> startInsertAfter(String targetId)
>
> But note these two are useless, because what we need is insert "inside" at
> the beginning
> or the end.
>
> For example:
>
> <head>
>
> <script .../>
> </head>
>
> <body>
> <script .../>
>
> </body>
>
> Please note there are some cases where the jsf third party library (for
> example
> primefaces) provides its own rules to to render the script at first,
> middle or last. But
> in this case it doesn't matter those rules, because what we really need is
> that the
> resource is added.
>
> The code that activates the flags to render head, body or form target is
> on
> h:outputScript and h:outputStylesheet, specifically in the listener
> attached to
> PostAddToViewEvent. This is the best place, because here is the place
> where
> UIViewRoot.addComponentResource is done.
>
>
>
> I think what's really important in this problem is provide the API where
> you can notify
> that the resource needs to be added. In MyFaces there is:
>
> class RequestViewContext {
>
> public boolean isRenderTarget(String target);
>
> public void setRenderTarget(String target, boolean value);
>
> }
>
> Which could evolve to something more complex, including the UIComponent
> instance and
> so on.
>
> I don't really like to do a list comparison, because it can introduce an
> unnecessary
> performance overhead. I would like an API that could have some methods to
> register the
> component for updated on the target and others to get the changes and do
> the necessary
> calculation in PartialViewContext.processPartial(...) (render response).
>
> regards,
>
> Leonardo Uribe
>