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

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

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Tue, 21 Jun 2016 14:37:19 +0200

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