myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Leonardo Uribe <>
Subject [jsr-314-open] PostAddToViewEvent publishing conditions
Date Thu, 18 Mar 2010 01:10:16 GMT
Actually the event publishing conditions of
PostAddToViewEvent/PreRemoveFromViewEvent are not very clear. These
conditions depends if partial state saving is used or not and if facelets
updates the view or  not. The problem is this fact is not very intuitive, so
for write code correctly the user needs to be aware of that, in other words
this should be documented somewhere. Also, there are some technical
questions that could be of interest on this mailing list and myfaces dev
list too.

In theory, PostAddToViewEvent is used in this cases:

- h:outputScript and h:outputStylesheet renderers uses it to relocate
component resources to UIViewRoot facet "head" or "body" target
(h:outputStylesheet by default to "head"). Note "head" and "body" facets are
- cc:insertChildren and cc:insertFacet, to relocate components to some place
inside cc:implementation. The reason why we do that through a listener
attached to PostAddToViewEvent is we need to take into account the case of
nested composite components using cc:insertFacet/cc:insertChildren. Note
when the component tree is built the first time, PostAddToViewEvent is
propagated from root to nodes.
- When Partial State Saving is enabled, it is necessary to attach a listener
to PostAddToViewEvent/PreRemoveFromViewEvent, so if some user add/remove a
component, we can keep track of those changes and save/restore the component
tree properly, in other words, support component addition or removal

Now, the instants of time where PostAddToViewEvent is published are:

- With Partial State Saving enabled

   * When the view is build at first time.
   * In a postback when the view is build but before the state of the
component is restored.

- With Partial State Saving disabled

   * When the view is build at first time.
   * In a postback when the view is "refreshed", because all component nodes
are detached and attached to the tree. In other words, on render response
phase, vdl.buildView is called and in this case facelets algorithm add all
transient components (usually all html markup not saved), and to do that, it
detach and attach all components to be in right order. This also has some
other implications, like c:if tag depends on this to work correctly.

A developer that is not aware of this fact could think that
PostAddToViewEvent is called when the view is build.

This is true with PSS is enabled, but is not true when PSS is disabled. On
this topic:

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

It was described why PostAddToViewEvent cannot be used in that case. But
let's change a litte bit the problem. We have a custom component that
creates some other on PostAddToViewEvent and add it as a children. It should
work but in fact it only works when PSS is enabled, with PSS disabled that
code will cause a duplicate id exception. Of course, the developer can solve
it with some effort but the point is we are not consider the element of
least surprise.

But this fact this is only the tip of the iceberg. In mojarra (this was
already fixed on myfaces), components relocated by cc:insertChildren or
cc:insertFacet does not have state when PSS is disabled, because when the
view is "refreshed" the components are created again, but facelets algorithm
remove all components with no bound to a facelet tag handler, and then the
listeners attached to PostAddToViewEvent relocates the new ones, so this
effect is not notice at first view.

Do you remember PostAddToViewEvent propagation ordering is important by
cc:insertChildren/cc:insertFacet? well, with PSS disabled, the ordering now
is from nodes to root, because all nodes are detached and attached from
nodes to root, so in myfaces we did something like this (I removed some non
relevant code to be more clear):

            if (refreshTransientBuild)
            // populate UIViewRoot
            _getFacelet(renderedViewId).apply(context, view);
            if (refreshTransientBuild)

                    // When the facelet is applied, all components are
removed and added from view,
                    // but the difference resides in the ordering. Since the
view is
                    // being refreshed, if we don't do this manually, some
tags like
                    // cc:insertChildren or cc:insertFacet will not work
correctly, because
                    // we expect PostAddToViewEvent will be propagated from
parent to child, and
                    // facelets refreshing algorithm do the opposite.


FaceletViewDeclarationLanguage._publishPostAddToViewEvent(context, view);

In few words, disable event processing, and fire PostAddToViewEvent and
PreRemoveFromViewEvent in the expected order to build the tree correctly. If
we don't do this, h:outputScript and h:outputStylesheet will not work
correctly (remember that UIViewRoot "head" and "body" facets are transient,
so if these components are relocated, are in fact transient too). Also, care
must be taken to prevent the listener of programatic component
addition/removal register components on this time.

The conclusion based on this reasoning, it is clear the need of new event
that be specific for relocate components (keep it simple for christ sake!).
This event should be propagated for all components in the tree after the
view is build from root to nodes. It could be good to have some
clearification about the real intention of
PostAddToViewEvent/PreRemoveFromViewEvent. Really, better names for those
events are PostAddToComponentTreeEvent/PreRemoveFromComponentTreeEvent.

Suggestions are welcome.


Leonardo Uribe

View raw message