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: Tue, 21 Jun 2016 17:22:17 +0200

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