myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Leonardo Uribe <lu4...@gmail.com>
Subject Re: Custom facelet tag handlers and ajax rendering problem
Date Fri, 08 Oct 2010 22:06:00 GMT
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 <atchertchian@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
>

Mime
View raw message