Issue Details (XML | Word | Printable)

Key: JAVASERVERFACES_SPEC_PUBLIC-770
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: Unassigned
Reporter: lu4242
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
javaserverfaces-spec-public

add component resources depending on the owner component state

Created: 17/Mar/10 07:17 AM   Updated: 20/Dec/13 05:28 PM
Component/s: Components/Renderers
Affects Version/s: 2.0
Fix Version/s: 2.3

Time Tracking:
Not Specified

Environment:

Operating System: All
Platform: All


Issuezilla Id: 770
Status Whiteboard:

size_large importance_small

Tags:
Participants: Ed Burns, lu4242 and rogerk


 Description  « Hide

All this text is extracted from jsr-314-open mailing list and it is put here as
reference.

[jsr-314-open] add component resources depending on the owner component state

Trying to use the new JSF 2.0 Resource Api I have found one case that maybe it
is of interest on this list.

Suppose a component that needs to add some specific javascript/css file based on
a value. In tomahawk we can do it something like this on the renderer:

if( tabbedPane.isClientSide() ){
addResource.addJavaScriptAtPosition(facesContext,
AddResource.HEADER_BEGIN, HtmlTabbedPaneRenderer.class, "dynamicTabs.js");

This code works because the response is buffered and post-processed.

The problem is how to do that with JSF 2.0?

One could think on use a listener attached to PreRenderComponentEvent, but this
event is called before encodeBegin, so it is useless in this case, because we
need to call it before the whole view is rendered.

Then, you can think do something like this:

@ListenerFor(systemEventClass = PreRenderViewEvent.class)
public class HtmlTabbedPaneRenderer extends HtmlRenderer
......

It does not work too. The reason is the listener is attached to the component
instance, and only the UIViewRoot instance receives the event. If we do the same
as f:event does when PreRenderViewEvent is found (suscribe the listener on
UIViewRoot) we lose the component we are referencing, because it is replaced by
UIViewRoot.

I can solve this one creating a wrapper for ViewHandler that publish a custom
event for all components before render view. but the big question is if jsf 2.0
should support this use case out of the box.

>> Cagatay Civici
>> Why not listening to PostAddToViewEvent with;
>>
>>
>> @ListenerFor(systemEventClass = PostAddToViewEvent.class)
>>public class HtmlTabbedPane extends UIComponentBase { >>}
>>
>> and in event listener;
>>
>> if(this.isClientSide()) {
>> viewroot.addComponentResource(...);
>>

Does not work too because in a postback, PostAddToViewEvent occur in the
following cases:

  • With Partial State Saving enabled when the view is build but before the state
    of the component is restored.
  • With Partial State Saving disabled when the view is "refreshed", because all
    component nodes are detached and attached to the tree.

Now suppose the component has a ValueExpression attached to the property, and
the value changes on Invoke Application Phase. With partial state saving the
tree was already build, so it doesn't catch the logic. With partial state saving
disabled, the event occur twice, the first time when the tree structure is
build, the second time when the view is updated, so again it doesn't work,
because if the resource component (it is transient too) was already added, it
cannot be removed later.

>> Ed Burns
>>
>>Please correct me if I'm wrong, but the problem you state above seems to
>>be just another example of the garden variety "conditional value changes
>>between requests or during a request".
>>

Yes and no. The difference with other examples of the garden is to add a
component resource, we need to do it in the right time, since it requires to
update the component tree adding components before render it, but after we know
which view will be rendered. Please note by a previous fix, all component
resources are transient.

As a side note, if someone wants to do this in a composite component he/she
probable do this:

<c:if test="#{cc.attrs.....}">
<h:outputStylesheet ..../>
</c:if>

And it works in myfaces because we have a fix for the c:if problem.

>> Do we need PreAddComponentResourceEvent and
>> PostAddComponentResourceEvent?

In my opinion is only necessary to add only one event. The right place is after
resolve PreRenderViewEvent, because only in that time we know the current
component tree is the view that will be rendered.

It is not possible to use PreRenderComponentEvent because it is called on
encodeBegin and the view header was already rendered.

It is not possible to use PreRenderViewEvent like this:

@ListenerFor(systemEventClass = PreRenderViewEvent.class)
public class HtmlTabbedPaneRenderer extends HtmlRenderer

because the listener is attached to the component when it is created, but the
event is only received by the current UIViewRoot. If we fix the algorithm on
Application class to attach this listener to the current UIViewRoot instance,
the source of the event will be UIViewRoot, and traverse the tree to find the
component is worse. Note other option is publish PreRenderViewEvent for all
child components, but after know the view that will be rendered.

In conclusion, for make this use case possible we need two things:

1. The right time to add the resource to the component tree.
2. The component with its state restored and updated.

Thinking more about it, another option about put this code could be on
vld.buildView method, but note when partial state saving is enabled this method
return immediately (maybe it is possible to change it).



Ed Burns added a comment - 18/May/10 02:10 PM

Move to 2.1


Ed Burns added a comment - 08/Jun/10 01:09 PM

triage


Ed Burns added a comment - 22/Jun/10 09:04 PM

rogerk


rogerk added a comment - 27/Oct/10 02:12 PM

triage


rogerk added a comment - 16/Nov/10 12:53 PM

triage