---------- Forwarded message ----------
From: Leonardo Uribe <lu4242(a)gmail.com>
Date: Sat, Sep 12, 2009 at 11:03 PM
Subject: [jsr-314-open] h:outputScript target="head" is rendered in body when
partial state
saving is used and there is a postback
To: jsr-314-comments(a)jcp.org
Hi
I have notice a failure when <h:outputScript target="head" ....> is used
and
there is a postback, that requires change something in the spec.
Suppose the following scenario
<h:head>
</h:head>
<h:body>
<h:outputScript name="jsf.js" library="javax.faces"
target="head"/>
<!-- some other stuff -->
</h:body>
The renderer documentation of h:outputScript and h:outputStylesheet says
this:
"....The implementation of this renderer must have a @ListenerFor annotation
attached to it, at the class level, declaring PostAddToViewEvent.class as
the value of the systemEventClass attribute. The presence of this annotation
on a renderer implies the renderer implements ComponentSystemEventListener,
which this renderer must do. The implementation of processEvent() must
extract the UIComponent from the argument event and look for the presence of
the key "target" in the component's attribute Map. If and only if such a
key
is present, the implementation of processEvent() must pass the component to
UIViewRoot.addComponentResource()...."
Now imagine the user are submitting a form and there is a validation error.
In that case there is a postback so the same view is rendered. The first
request build a view like this:
<h:head>
<h:outputScript name="jsf.js" library="javax.faces"
target="head"/>
</h:head>
<h:body>
<!-- some other stuff -->
</h:body>
but the next one restore this if partial state saving is used:
<h:head>
</h:head>
<h:body>
<h:outputScript name="jsf.js" library="javax.faces"
target="head"/>
<!-- some other stuff -->
</h:body>
the reason why this happens is clear looking the documentation of
UIComponent.getChildren():
"...After the child component has been added to the view, if the following
condition is not met:
FacesContext.isPostback() returns true and
FacesContext.getCurrentPhaseId() returns PhaseId.RESTORE_VIEW..."
This is valid if we are using jsf 1.2 state saving, because we save and
restore the whole tree, so the components created are already on the view.
But when we use partial state saving, we do this operation:
restored view = apply delta on (fresh view from VDL.buildView() + added
components after that - removed components after that)
The conclusion is: All components that relies on
ComponentSystemEventListener listening PostAddToViewEvent to be relocated in
some way (h:outputScript, h:outputStylesheet, composite:insertChildren,
composite:insertFacet (because we need the whole tree built before move
components, otherwise nested composite components will not work correctly) )
are incompatible with partial state saving. Its more, if the affected
components are moved outside/inside/between NamingContainer or
UniqueIdVendor instances the state will not be correctly restored.
The solution from my point of view is relax the restriction on
UIComponent.getChildren() (maybe adding a faces context attribute saying the
current view is being built using partial state saving), so when partial
state saving is used, PostAddToViewEvent will be published.
I'll appreciate a lot any comment.
regards
Leonardo Uribe