[ 
https://issues.apache.org/jira/browse/MYFACES-3733?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13774454#comment-13774454
 ] 

Xavier Cho commented on MYFACES-3733:
-------------------------------------

@[~lu4242], I tried to change ordering of components to make a child added 
before the parent as suggested, though the problem seems to persist.

I'd very much like to contribute a test case, though I had to abandon the 
current approach of dynamically creating composite components altogether as I'm 
already far behind the schedule.

I'm sorry I couldn't submit any concrete example which reproduces the problem. 
And thanks for the help!
                
> Implement vdl.createComponent(...)
> ----------------------------------
>
>                 Key: MYFACES-3733
>                 URL: https://issues.apache.org/jira/browse/MYFACES-3733
>             Project: MyFaces Core
>          Issue Type: Task
>          Components: JSR-344
>            Reporter: Leonardo Uribe
>
> This is a difficult issue to do in JSF 2.2 . I have spent a long time to 
> solve this one, and given the complexity involved and since there is no 
> documentation anywhere about how this should work, I'll let the required 
> explanation here.
> The idea is allow to include generated vdl fragments into pages 
> programatically. This includes normal components, composite components or 
> just fragments of markup. The method signature is this:
> public UIComponent createComponent(FacesContext context, String taglibURI, 
> String tagName, Map<String,Object> attributes)
> Some valid examples of this are:
> // Normal component
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/html";, 
>     "outputText", attributes);
> // Composite component
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/composite/testComposite";, 
>     "dynComp_1", attributes);
> // Dynamic include
> Map<String, Object> attributes = new HashMap<String, Object>();
> attributes.put("src", "/addSimpleIncludeVDL_1_1.xhtml");
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/facelets";, 
>     "include", attributes);
> The javadoc does not suggest the dynamic include is valid, but I think users 
> expect these kind of stuff work. 
> Theoretically it sounds like something easy to do, but unfortunately it is 
> not. The reasons why this is so are:
> - Facelets algorithm wraps html markup into UILeaf instances, which is a 
> special "transient" component. UILeaf instances are never saved or restored 
> from the component tree, but in some points of the algorithm (restore view 
> and before render response when vdl.buildView() is called) the component tree 
> is updated, adding or removing UILeaf instances.
> - Facelets has an algorithm that require id generation to be stable, 
> otherwise a duplicate id exception may arise. A lot of effort has been done 
> to organize this part, and the current solution works very well. But in this 
> case, we need to generate unique ids that can be refreshed somehow.
> - Facelets algorithm has an special logic to deal with dynamic sections like 
> the ones generated by c:if or 
> ui:include src="#{...}" . Add facelets sections programatically could make 
> this algorithm fail, removing sections that should be.
> - Facelets PSS algorithm needs to be taken into account too. The listener 
> that is used to register programmatic changes on the tree in 
> DefaultFaceletsStateManagementStrategy uses  ComponentSupport.MARK_CREATED to 
> identify which component belongs to the component tree and which one was 
> added by outside. Add facelets sections programatically could make this 
> algorithm fail, because it could assume some sections of the tree does not 
> need to be saved fully, even if that's not true.
> The issue in the spec is this:
> https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-611
> At start the idea was to export FaceletFactory directly, but I told to the EG 
> that it was a bad idea by multiple reasons (That's a Pandora's Box). See:
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-11/message/91
> This previous message is useful too:
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-06/message/18
> After thinking and trying different strategies to overcome this issue, I 
> finally found the following solution:
> - Use the compiler for generate a custom Facelet "inline" or "on the fly". It 
> is not necessary to create an
> xml document and then parse it, just generate the Tag class and pass it to 
> the compiler to generate an
> Abstract Syntax Tree (AST), with the hierarchy of facelet TagHandler 
> instances.
> - To solve the issue with UILeaf instances, the best is create a stateful 
> ComponentSystemEventListener that on restore view phase it compiles the 
> custom Facelet and apply it over the fragment. The ideal and only event to 
> attach the listener is PostRestoreStateEvent, but we need to add the code in 
> UIComponent.processEvent().
> - In the case of a ui:include, if multiple components are returned, all of 
> them are grouped into a single
> UIPanel. If the code returns one component, it returns that component.
> - If the code generates a branch, or in other words, multiple nested 
> components, it should attach the 
> listener to deal with UILeaf instances, if it just generates one component do 
> not do that because it is
> not necessary.
> - To solve the issue with the ids, just call UIViewRoot.createUniqueId() and 
> use the generated value to derive unique facelets ids. The new algorithm that 
> generate ids is very flexible and it will support this case. This base key 
> should be saved in the state so the same ids are generated for the same 
> fragment.
> - Support composite components needs special treatment. The idea is support 
> something like this:
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/composite/testComposite";, 
>     "dynComp_1", attributes);
>     
> // .... add children / facets to the algorithm
> someComponentInTheView.getChildren().add(component);
> In this case the "processing" of the composite component content must be done 
> only when the component is added to the view. The idea is vdl.createComponent 
> only create the root component class, and then use a listener attached to 
> PostAddToViewEvent to process the content. We need to modify the algorithm, 
> because in this case children/facets are created programatically and not 
> using facelets algorithm. The idea is add an extra facelet in the compiler to 
> detect when the result is a composite component. The listener attached to 
> PostAddToViewEvent must be done in a way that only works on the first 
> addition to PostAddToViewEvent.
> - If the call to vdl.createComponent() occur when there is an active 
> FaceletCompositionContext instance, reuse that instance doing the necessary 
> changes in the context, otherwise instantiate a clean context.
> - Facelets PSS algorithm will work just fine as long as the returned 
> component does not have ComponentSupport.MARK_CREATED set when the view is 
> refreshed, saved or restored. It is enough to just use the component 
> attribute map.
> - The two base cases to test are:
>    * Create components programatically inside a "binding" method.
>    * Create components programatically in PreRenderViewEvent or in the 
> Renderer.
>   The difference is the "binding" occur when there is a 
> FaceletCompositionContext instance active, but in the other cases there is no 
> active instance.
> Comply with all previous requirements can be difficult, but it is very 
> important, otherwise the algorithm will not be stable enough.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to