jsr344-experts@javaserverfaces-spec-public.java.net

[jsr344-experts] Re: [jsr-344-experts] FaceletFactory.createComponent(...) ?

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Tue, 12 Jun 2012 17:55:22 +0200

Hi

In my opinion the rules to handle programatically added components are:

1. Never move or delete a component bound to a facelet tag anywhere.
Use c:if, ui:include src="#{...}" for that. Manipulation of those
component should be done through facelet tags only.
2. You can use PostAddToViewEvent to add components, but remember do
not include any logic there (if something add otherwise continue),
otherwise it will break PSS principle "... buildView() call should be
able to reconstruct the same component tree ...".
3. PreRenderViewEvent is ideal to do component manipulation and
include logic (see Acid test for components doing that hack).

http://svn.apache.org/repos/asf/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/component/

That trick is similar to the one used in tomahawk (see
org.apache.myfaces.tomahawk.application.PreRenderViewAddResourceEvent).
After doing the performance stuff, I can say the cost in a visitTree()
call with SKIP_ITERATION hint for this event is minimal, compared with
the buildView and renderView time. And it is nice to use annotations
in this part too.

4. Override ComponentHandler.onComponentCreated and
onComponentPopulated are also good places to add components based in
some logic. For example see:

http://svn.apache.org/repos/asf/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableTagHandler.java

This trick has the same limitations as PostAddToViewEvent.

5. It is also valid to add components in renderers, usually set
transient="true" (remember since the component is transiet, add
decode() logic).

If you follow those rules, programatically added components will work
as expected.

But add a component from a backing bean requires specify when and how to do it.

regards,

Leonardo Uribe

2012/6/12 Leonardo Uribe <lu4242_at_gmail.com>:
> Hi
>
> I have found this weird example:
>
> http://stackoverflow.com/questions/10853177/how-to-use-the-includehandler-inside-my-custom-component
>
> The revelant code is this:
>
> <h:form>
>    <h:commandButton value="include" action="#{bean.include}" />
> </h:form>
> <h:panelGroup id="include" />
>
> public void include() throws IOException {
>    FacesContext facesContext = FacesContext.getCurrentInstance();
>    FaceletContext faceletContext = (FaceletContext)
> facesContext.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
>    faceletContext.includeFacelet(facesContext.getViewRoot().findComponent("include"),
> "include.xhtml");
> }
>
> The reporter says that hack works, but it shouldn't. Why? because
> FaceletContext should only live on build view time, and the action
> occur in invoke application phase. The template engine creates
> FaceletContext instance when process composite components or apply
> templates, so the FaceletContext instance used here is the last one
> used. Which one is? it could be anyone.
>
> And? well, expose FaceletFactory or add a method like
> FaceletFactory.createComponent(...) leave the spec open to these kind
> of ugly hacks.
>
> But note the interesting part behind this trick. What the user want is
> a way to "inject" programatically "something" into the component tree
> from a bean. Isn't that the intention behind
> JAVASERVERFACES_SPEC_PUBLIC-599? of course!.
>
> This issue becomes related to the conditions where you can manipulate
> the component tree programmatically, specifically "when" it is valid
> to do such manipulation. If the intention is add a composite component
> directly in a component tree, the call must warrant the generated ids
> used should be stable and no facelet generated ids should be used at
> all, so the algorithm can consider this as a programmatic addition
> (the algorithm for c:if addition/deletion should skip them).
>
> regards,
>
> Leonardo Uribe
>
> 2012/6/12 Leonardo Uribe <lu4242_at_gmail.com>:
>> Hi
>>
>> Checking some JSF 2.2 javadoc, I notice this method:
>>
>> UIComponent FaceletFactory.createComponent(String taglibURI, String
>> tagName, Map<String,Object> attributes)
>>
>> There is no javadoc, only a reference to this issue:
>>
>> http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-599
>> Application.createComponent(): compcomp via namespace+compname
>>
>> The issue is closed as fixed, but there is not any description at all.
>> This raises some questions?
>>
>> 1. Is that the right place to put that method? The description of
>> FaceletFactory says this:
>>
>> ".... FaceletFactory for producing Facelets relative to the context of
>> the underlying implementation. ..."
>>
>> It does not have sense to me. If I want a way to create a component
>> using the namespace/compname or something like that, Isn't that
>> responsibility more related to the ViewDeclarationLanguage?
>>
>> 2. Why expose FaceletFactory? In my opinion, FaceletFactory is a
>> detail of facelets VDL implementation, right? The "power" to create
>> facelets is from facelets VDL. If that so, why don't include an
>> abstract class called
>>
>> public abstract class
>> javax.faces.view.facelets.FaceletsViewDeclarationLanguage extends
>> javax.faces.view.ViewDeclarationLanguage
>>
>> and add the methods that are now in FaceletFactory there? Maybe
>> createComponent(String taglibURI, String tagName, Map<String,Object>
>> attributes) qualify to be included directly in
>> ViewDeclarationLanguage.
>>
>> 3. JAVASERVERFACES_SPEC_PUBLIC-599 description says something like this:
>>
>>>> I feel there is value in the ability to instantiate an EzComp object
>>>> in Java so that a component tree can be built in a backing bean (or
>>>> other Class). JSF must be doing this in the background through
>>>> Facelets, so providing this ability in Java should not be too difficult.
>>
>> the problem here is it does not specify where and when the component
>> is created: inside a component "binding" property? in a
>> @PostConstruct? in an actionListener?. For each one there are
>> different considerations. Also, it is necessary to check that the
>> generated ids does not clash with other existing ones in the component
>> tree. I can see this is only the tip of the iceberg.
>>
>> 4. Is this topic really closed? Some months ago I sent a message with
>> this topic:
>>
>> [jsr344-experts] JAVASERVERFACES_SPEC_PUBLIC-611 FIXED expose
>> FaceletFactory and Facelet classes
>>
>> No response so far.
>>
>> Suggestions are welcome.
>>
>> regards,
>>
>> Leonardo Uribe