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: Bauke Scholtz <balusc_at_gmail.com>
Date: Wed, 22 Jun 2016 09:02:39 +0200

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> 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> 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>
> 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>:
>
> 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>:
>
> 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
>
>
>
>
>
>
>
>