[ https://issues.apache.org/jira/browse/MYFACES-3733?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Leonardo Uribe resolved MYFACES-3733. ------------------------------------- Resolution: Fixed Fix Version/s: 2.2.0 Assignee: Leonardo Uribe > 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 > Assignee: Leonardo Uribe > Fix For: 2.2.0 > > > 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