You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by Anahide Tchertchian <at...@nuxeo.com> on 2010/10/08 17:47:07 UTC

Custom facelet tag handlers and ajax rendering problem

Hi,

This is not a MyFaces issue, but i've been advised to post here since
i'm more likely to find people interested and knowledgeable in
facelets interaction with ajax.

Facelets is an amazing product, and I've been very pleased to use it
to develop a layout system, generating forms to present documents
metadata in different modes (create, edit, view...), and it works very
well, except when performing some kinds of ajax re-rendering.

I'm using facelet tag handlers that will dynamically build the
subsequent handlers to use from an external XML configuration. When
doing so, i'm exposing some variables to the EL context (via the
variable mapper) so that i can use these variables in xhtml templates
referenced in the XML config. These variables resolve to objects that
are only available in the context of my tag handler.

My problem is that the default ComponentHandler class "caches"
components (see ComponentSupport#findChildByTagId): it will not
re-create the component if it is already found in the view, and this
is what is happening when doing ajax re-rendering. The component is
displayed, but as its properties have not been re-created, they hold
references to the old variables that the tag handler previously
exposed in the context.

The main classes and template performing the rendering are here:
http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-forms-layout-client/src/main/java/org/nuxeo/ecm/platform/forms/layout/facelets/LayoutTagHandler.java
http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-forms-layout-client/src/main/java/org/nuxeo/ecm/platform/forms/layout/facelets/LayoutRowTagHandler.java
http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-forms-layout-client/src/main/java/org/nuxeo/ecm/platform/forms/layout/facelets/LayoutRowWidgetTagHandler.java
http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-webapp-base/src/main/resources/nuxeo.war/layouts/layout_default_template.xhtml

For instance the LayoutTagHandler class uses a xhtml template (see
above) that contains the tag <h:outputText value="#{widget.label}" />.
The "widget" variable is exposed in the context by the
LayoutRowWidgetTagHandler class. The UIOutput component is not being
re-created, so it holds references to the old "widget" value exposed
in the variable mapper. On the other hand, displaying #{widget.label}
without the UIOutput component will work as expected.

I've been trying to "hack" the detection of pre-existing components by
computing custom identifiers in TagConfig instances used by my tag
handlers, but this is not enough for JSF tags present in the layout
template as above, as i'm letting the default facelets system handle
them (via ctx.includeFacelet(parent, template)).

So I would be interested if anyone had some suggestions or ran into
the same problem and found a good solution. Do I need to re-define the
generic ComponentHandler so that it does not perform caching in my use
cases (I'd rather not)? Do i need to make variables available in the
context differently?...

Please do not hesitate to tell if you'd rather have a minimal example
on how to reproduce my problem: i'd be happy to provide it. If it
makes a difference, i'm currently using version 1.1.11 of facelets,
and will be migrating to version 1.1.15 very shortly (and using
version 1.2_09 of sun jsf library).

Thanks a lot in advance!
anahide.

PS: if some people are interested, documentation of the feature is
here: http://doc.nuxeo.com/display/NXDOC/Layouts

Re: Custom facelet tag handlers and ajax rendering problem

Posted by Anahide Tchertchian <at...@nuxeo.com>.
Hi,

Thanks a lot for your answer, i've got more questions below so that i
can follow your advice correctly.

On 9 October 2010 00:06, Leonardo Uribe <lu...@gmail.com> wrote:
>
> Note VariableMapper has "build time scope", or in other words, it is set
> and discarded when facelets builds the component tree. The most easy
> example is try to set some variable and then try to retrieve it on
> render response.
>
> When that "build time scope" happens? On facelets 1.1.x happens when
>
> 1. A component tree is build from facelets abstract syntax tree (AST).
> 2. A component tree is refreshed (postback before render view).
>
> At first view, the only way to make a "VariableMapper" that works on
> all events is do something similar to tomahawk t:aliasBean. On JSF 1.2 and
> JSF 1.1, this component only  works fully for MyFaces but with JSF 2.0,
> now it works for Mojarra (RI). It is possible to use it on
> on 1.x and Mojarra, but with limitations (binding property assigment
> will not be wrapped).

Unfortunately, this is no go for me unless i have a way to generate
automatically backing bean on demand: in theory there can be any
number of rendering of a given layout on the same page. Using the
VariableMapper to store my temporary objects was a good workaround
until now, as this is working ok in all cases except for ajax
interactions.

> On facelets 1.1.x, when there is a postback the view is refreshed to
> addd transient components (usually html markup) and handle c:if case
> (that's a long story). It is expected existing components created
> on RestoreView phase to be only "updated", but in your case what you can
> see is the component does not change (for more information see
> com.sun.facelets.tag.jsf.ComponentHandler class method apply() ).

Well my tags are the one actually changing the component tree: i'm
generating and applying ComponentHandler instances according to custom
rules, so maybe i'm interested about the long story for the c:if case.
If this tag is allowed to change the component tree structure on an
ajax call, i was assuming that i could too, and i was not expecting
the components to hold references to the context when they were first
added to the tree. I guess i'm also a little confused because what you
are describing is a mere refresh of the view, whereas i thought ajax
was performing the whole JSF lifecycle on parts of the component tree
(and so was recreating components from scratch anyway).

As i'm a bit worried about breaking the facelets or JSF expectations
of what a tag handler is allowed to do, i'd like to know if the
ComponentHandler behaviour (trying to find existing components, not
settings their attributes again if found) is just this tag handler
contract, or if it needs to have that behaviour for other reasons that
i do not see: what's the harm in re-creating components from scratch
(or at least setting their attributes again), except performance
gained from using the cached objects?

> In theory, #{widget.label} will be evaluated on render time, but
> VariableMapper only is available on build time.

I think this is working in practice because the
UIInstructionTagHandler does re-create its associated component
instead of taking the one already in the component tree.

> My solution would be use t:aliasBean or if this is required for a component
> internally, mix t:aliasBean and the target component, so #{widget.label}
> could
> be resolved correctly.

Ok, so i'm thinking that i could make my tag tag handler create a JSF
component that will hold the values that i'm already exposing in the
VariableMapper. It could expose them again in the context in other JSF
phases after the build. I'll look into t:aliasBean to make other
components resolve their expressions by using this component.
Of course, i'll need to update the values held by this component on
ajax re-render, that's why i'd like to know if it's risky or unfit to
do so: if it's not, i will create it by hand instead of using the
standard ComponentHandler implementation.

Thanks again !
Regards,
anahide.

Re: Custom facelet tag handlers and ajax rendering problem

Posted by Leonardo Uribe <lu...@gmail.com>.
Hi

The problem(s) here are not an easy topic, but I think it is worth to
take some time and explain what's going on, since this is relevant
for JSF 2.

AT>> I'm using facelet tag handlers that will dynamically build the
AT>> subsequent handlers to use from an external XML configuration. When
AT>> doing so, i'm exposing some variables to the EL context (via the
AT>> variable mapper) so that i can use these variables in xhtml templates
AT>> referenced in the XML config. These variables resolve to objects that
AT>> are only available in the context of my tag handler.

Note VariableMapper has "build time scope", or in other words, it is set
and discarded when facelets builds the component tree. The most easy
example is try to set some variable and then try to retrieve it on
render response.

When that "build time scope" happens? On facelets 1.1.x happens when

1. A component tree is build from facelets abstract syntax tree (AST).
2. A component tree is refreshed (postback before render view).

At first view, the only way to make a "VariableMapper" that works on
all events is do something similar to tomahawk t:aliasBean. On JSF 1.2 and
JSF 1.1, this component only  works fully for MyFaces but with JSF 2.0,
now it works for Mojarra (RI). It is possible to use it on
on 1.x and Mojarra, but with limitations (binding property assigment
will not be wrapped).

AT>> My problem is that the default ComponentHandler class "caches"
AT>> components (see ComponentSupport#findChildByTagId): it will not
AT>> re-create the component if it is already found in the view, and this
AT>> is what is happening when doing ajax re-rendering. The component is
AT>> displayed, but as its properties have not been re-created, they hold
AT>> references to the old variables that the tag handler previously
AT>> exposed in the context.

On facelets 1.1.x, when there is a postback the view is refreshed to
addd transient components (usually html markup) and handle c:if case
(that's a long story). It is expected existing components created
on RestoreView phase to be only "updated", but in your case what you can
see is the component does not change (for more information see
com.sun.facelets.tag.jsf.ComponentHandler class method apply() ).

AT>> For instance the LayoutTagHandler class uses a xhtml template (see
AT>> above) that contains the tag <h:outputText value="#{widget.label}" />.
AT>> The "widget" variable is exposed in the context by the
AT>> LayoutRowWidgetTagHandler class. The UIOutput component is not being
AT>> re-created, so it holds references to the old "widget" value exposed
AT>> in the variable mapper. On the other hand, displaying #{widget.label}
AT>> without the UIOutput component will work as expected.

In theory, #{widget.label} will be evaluated on render time, but
VariableMapper only is available on build time.

AT>> I've been trying to "hack" the detection of pre-existing components by
AT>> computing custom identifiers in TagConfig instances used by my tag
AT>> handlers, but this is not enough for JSF tags present in the layout
AT>> template as above, as i'm letting the default facelets system handle
AT>> them (via ctx.includeFacelet(parent, template)).

That's not going to work.

AT>> So I would be interested if anyone had some suggestions or ran into
AT>> the same problem and found a good solution. Do I need to re-define the
AT>> generic ComponentHandler so that it does not perform caching in my use
AT>> cases (I'd rather not)? Do i need to make variables available in the
AT>> context differently?...

My solution would be use t:aliasBean or if this is required for a component
internally, mix t:aliasBean and the target component, so #{widget.label}
could
be resolved correctly.

regards,

Leonardo Uribe

2010/10/8 Anahide Tchertchian <at...@nuxeo.com>

> Hi,
>
> This is not a MyFaces issue, but i've been advised to post here since
> i'm more likely to find people interested and knowledgeable in
> facelets interaction with ajax.
>
> Facelets is an amazing product, and I've been very pleased to use it
> to develop a layout system, generating forms to present documents
> metadata in different modes (create, edit, view...), and it works very
> well, except when performing some kinds of ajax re-rendering.
>
> I'm using facelet tag handlers that will dynamically build the
> subsequent handlers to use from an external XML configuration. When
> doing so, i'm exposing some variables to the EL context (via the
> variable mapper) so that i can use these variables in xhtml templates
> referenced in the XML config. These variables resolve to objects that
> are only available in the context of my tag handler.
>
> My problem is that the default ComponentHandler class "caches"
> components (see ComponentSupport#findChildByTagId): it will not
> re-create the component if it is already found in the view, and this
> is what is happening when doing ajax re-rendering. The component is
> displayed, but as its properties have not been re-created, they hold
> references to the old variables that the tag handler previously
> exposed in the context.
>
> The main classes and template performing the rendering are here:
>
> http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-forms-layout-client/src/main/java/org/nuxeo/ecm/platform/forms/layout/facelets/LayoutTagHandler.java
>
> http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-forms-layout-client/src/main/java/org/nuxeo/ecm/platform/forms/layout/facelets/LayoutRowTagHandler.java
>
> http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-forms-layout-client/src/main/java/org/nuxeo/ecm/platform/forms/layout/facelets/LayoutRowWidgetTagHandler.java
>
> http://hg.nuxeo.org/nuxeo/nuxeo-jsf/file/5.4/nuxeo-platform-webapp-base/src/main/resources/nuxeo.war/layouts/layout_default_template.xhtml
>
> For instance the LayoutTagHandler class uses a xhtml template (see
> above) that contains the tag <h:outputText value="#{widget.label}" />.
> The "widget" variable is exposed in the context by the
> LayoutRowWidgetTagHandler class. The UIOutput component is not being
> re-created, so it holds references to the old "widget" value exposed
> in the variable mapper. On the other hand, displaying #{widget.label}
> without the UIOutput component will work as expected.
>
> I've been trying to "hack" the detection of pre-existing components by
> computing custom identifiers in TagConfig instances used by my tag
> handlers, but this is not enough for JSF tags present in the layout
> template as above, as i'm letting the default facelets system handle
> them (via ctx.includeFacelet(parent, template)).
>
> So I would be interested if anyone had some suggestions or ran into
> the same problem and found a good solution. Do I need to re-define the
> generic ComponentHandler so that it does not perform caching in my use
> cases (I'd rather not)? Do i need to make variables available in the
> context differently?...
>
> Please do not hesitate to tell if you'd rather have a minimal example
> on how to reproduce my problem: i'd be happy to provide it. If it
> makes a difference, i'm currently using version 1.1.11 of facelets,
> and will be migrating to version 1.1.15 very shortly (and using
> version 1.2_09 of sun jsf library).
>
> Thanks a lot in advance!
> anahide.
>
> PS: if some people are interested, documentation of the feature is
> here: http://doc.nuxeo.com/display/NXDOC/Layouts
>