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

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

From: Cagatay Civici <cagatay.civici_at_gmail.com>
Date: Wed, 22 Jun 2016 10:19:06 +0300

Sure, actually Thomas Andraschko, the reporter of JAVASERVERFACES_SPEC_PUBLIC-1423 has implemented it in PF, I’ve asked him for details and his response briefly is;
> Have a look at:
> org.primefaces.application.resource.DynamicResourcesPhaseListener - it collects the initial resources on the ajax request
> org.primefaces.context.PrimePartialResponseWriter - ln 278; gets the resources again, creates a diff of the initial and new resources, render some JS to load the resources dynamically
Regards,

Cagatay Civici
PrimeFaces Lead
PrimeTek Informatics


On Wednesday 22 June 2016 at 10:02, Bauke Scholtz wrote:

> Cagatay, can you point out it in the PrimeFaces codebase? Then I will look at similarities and differences and if necessary optimize the standard solution based on that.
> Cheers, B
>
> On Wed, Jun 22, 2016, 08:43 Cagatay Civici <cagatay.civici_at_gmail.com (mailto:cagatay.civici_at_gmail.com)> wrote:
> > We also have a custom solution to this in PrimeFaces as it is a very common case. A standard solution would be preferred for sure.
> >
> > Regards,
> >
> > Cagatay Civici
> > PrimeFaces Lead
> > PrimeTek Informatics
> >
> >
> > On Tuesday 21 June 2016 at 22:34, Bauke Scholtz wrote:
> >
> > > Coming to it, a new encodeAjax() method in UIComponent and Renderer (I'd rather call it encodePartial(), in line with the existing API) is a great suggestion which could indeed solve this problem and has potential for solving/optimizing ajax based problems in a much more flexible way. This is indeed a small and easy change in the API (and impl).
> > >
> > > +1 from me.
> > >
> > > Cheers, B
> > >
> > >
> > >
> > >
> > > On Tue, Jun 21, 2016 at 5:22 PM, Bauke Scholtz <balusc_at_gmail.com (mailto:balusc_at_gmail.com)> wrote:
> > > > As to writer.startEval(), as per https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1412 I've added a PartialViewContext#getEvalScripts() for JSF 2.3. This could be used.
> > > >
> > > > I will think about encodeAjax() and reply later.
> > > >
> > > > Cheers, B
> > > >
> > > > On Tue, Jun 21, 2016 at 4:01 PM, Leonardo Uribe <leonardo.uribe_at_irian.at (mailto:leonardo.uribe_at_irian.at)> wrote:
> > > > > Hi
> > > > >
> > > > > I just wanted to repost a comment from MyFaces issue tracker in 2012 about the issue being discussed now:
> > > > >
> > > > >
> > > > > Luciano Deiru:
> > > > >
> > > > > Thanks for the reply.
> > > > >
> > > > > I don't think it's going to be possible for me to migrate my app to JSF2 without a
> > > > > complete re-write of the frontend. I did a little more digging to see if richfaces was
> > > > > the issue and i can across this section of the richfaces 4.2.2 development guide...
> > > > > ...
> > > > >
> > > > > "JSF 2 does not allow resources such as JavaScript or Cascading Style Sheets (CSS) to
> > > > > be added if the element requiring the resource is not initially present in the JSF
> > > > > tree. As such, components added to the tree via Ajax must have any required resources
> > > > > already loaded. In RichFaces, any components added to the JSF tree should have
> > > > > components with corresponding resources included on the main page initially. To
> > > > > facilitate this, components can use the rendered="false" setting to not be
> > > > > rendered on the page."
> > > > > ...
> > > > >
> > > > > So the only workaround would be to add every possible richfaces and primefaces component
> > > > > to the login page so it loads the JS and css. That's a bit depressing because you
> > > > > can't create a "pure ajax"/dynamic application in JSF2 even tho i was possible in JSF1.
> > > > > It seems like a widely used approach and there are a lot of forum posts about the
> > > > > issue but no one is ever able to get it working without including the hidden components.
> > > > >
> > > > >
> > > > >
> > > > > regards,
> > > > >
> > > > > Leonardo Uribe
> > > > >
> > > > > 2016-06-21 15:55 GMT+02:00 Leonardo Uribe <leonardo.uribe_at_irian.at (mailto:leonardo.uribe_at_irian.at)>:
> > > > > > Hi
> > > > > >
> > > > > > I see.
> > > > > >
> > > > > > BS>> Also, to avoid duplicates and/or unnecessary re-rendering, all so far rendered
> > > > > > BS>> resources must be remembered in JSF state. AFAIC this is currently not the case.
> > > > > >
> > > > > > Well, this is information related to the view that is implicit there, but you want to
> > > > > > avoid to put it in the state.
> > > > > >
> > > > > > That's the reason why I like the way it is working in MyFaces, because we just keep
> > > > > > track of the change, rather than store that information in the state.
> > > > > >
> > > > > > Write the script directly into the <eval> is possible, but right now from spec
> > > > > > perspective there are not the necessary "building blocks" available.
> > > > > >
> > > > > > Both h:outputScript and h:outputStylesheet are components that are supposed to be
> > > > > > rendered as resources, but both components have Renderer instances, which are
> > > > > > the classes who finally decide how they should be rendered. Even if you have a list,
> > > > > > this list could include javascript/css or whatever and the resources could be rendered
> > > > > > in different ways.
> > > > > >
> > > > > > For example an h:outputScript could have children that could be js code. Or a
> > > > > > h:outputStylesheet could have a "media" property set.
> > > > > >
> > > > > > So if we want a "general" solution to the problem, which includes script, stylesheets
> > > > > > or whatever, it is necessary to know the component instance that needs to be added
> > > > > > or updated, and then ask the component how it should be rendered.
> > > > > >
> > > > > > The problem from the "building blocks" perspective, is there is no way the component
> > > > > > can know the current request is an ajax update. encodeAll(...) just means encode
> > > > > > everything and don't ask questions.
> > > > > >
> > > > > > I would like to have a method in UIComponent/Renderer called encodeAjax(...) for the
> > > > > > component, which by default start and end an <update> section and call by default
> > > > > > to encodeAll(...). Something like this
> > > > > >
> > > > > > public void encodeAjax(FacesContext _facesContext, PartialResponseWriter writer) {
> > > > > > try
> > > > > > {
> > > > > > writer.startUpdate(target.getClientId(_facesContext));
> > > > > > target.encodeAll(_facesContext);
> > > > > > }
> > > > > > finally
> > > > > > {
> > > > > > writer.endUpdate();
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > This part is done in PartialViewContext implementation, but if we can move it here
> > > > > > we could ask the component in a gentle way how it should be rendered. Then, from
> > > > > > this location we could override the method and call writer.startEval(...), which is
> > > > > > something is not possible in this moment, because there is no way right now to
> > > > > > customize this step for each component.
> > > > > >
> > > > > > In this way, you could add an script to load the stylesheet and then use the eval
> > > > > > to include it in the response.
> > > > > >
> > > > > > I have already mention this improvement in this mail some time ago:
> > > > > >
> > > > > > https://java.net/projects/javaserverfaces-spec-public/lists/jsr372-experts/archive/2015-04/message/41
> > > > > >
> > > > > > It looks we have yet another use case where this could be useful.
> > > > > >
> > > > > > regards,
> > > > > >
> > > > > > Leonardo Uribe
> > > > > >
> > > > > >
> > > > > > 2016-06-21 14:51 GMT+02:00 Bauke Scholtz <balusc_at_gmail.com (mailto:balusc_at_gmail.com)>:
> > > > > > > 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 (mailto: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
> > > > > >
> > > > >
> > > >
> > >
> >