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

Leonardo Uribe commented on MYFACES-2502:
-----------------------------------------

Yes, with the latest patch the problem is no more present. I think is the best 
we can do for solve this issue (really I spent a lot of time finding it and 
solving it).

Suggestions are welcome.

> Component state is lost for composite component childs of facets relocated by 
> composite:insertChildren or composite:insertFacet
> -------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: MYFACES-2502
>                 URL: https://issues.apache.org/jira/browse/MYFACES-2502
>             Project: MyFaces Core
>          Issue Type: Task
>          Components: JSR-314
>    Affects Versions: 2.0.0-beta
>            Reporter: Leonardo Uribe
>            Assignee: Leonardo Uribe
>         Attachments: MYFACES-2502-2.patch, MYFACES-2502-3.patch
>
>
> When partial state saving is not used, component state is lost for composite 
> component childs of facets relocated by composite:insertChildren or 
> composite:insertFacet
> To understand why this is happening, it is necessary to understand how 
> facelets works in context and how composite:insertChildren and 
> composite:insertFacet works, so I'll do a resume for it.
> In jsf 1.2, a facelet is applied in two cases:
> 1. When a page is request for first time, the whole component tree is build.
> 2. On a postback to update transient components like facelets UIInstruction.
> To see it in context, suppose a simple app that ask for a name an it print it 
> on another component on the same page:
> <h:form>
>   Name: <h:inputText value="{bean.name}/>
>   Previous Name: <h:outputtext value="{bean.name}/>
>   <h:commandButton value="submit" action="submitToThisSamePage"/>
> </h:form>
> That is what happened when facelets + jsf 1.2 is used:
>  First Request:
>  - There is a call to FaceletViewHandler.buildView from 
> FaceletViewHandler.renderView that cause the UIViewRoot instance to be filled 
> for first time calling to f.apply().
>  - The view is rendered.
>  - Save the state for all non transient components found on the view. This 
> include save the tree structure too, so it can be reconstructed later.
>  Postback (The user send his name and do a submit):
>  - Restore the tree structure and component state for all saved components.
>  - All lifecycle phases continues until before renderView
>  - On FaceletViewHandler.renderView there is a call to buildView, and this 
> one causes all transient components like facelets UIInstruction to be added 
> to the tree. ComponentHandler first try to detect if the component is on the 
> view before create it, and if that is true do not create it, instead it takes 
> this instance and continue apply the taghandles and it remove and add it from 
> tree, to give the chance to other transient components to be created and 
> added correctly.
>  - The view is rendered.
>  - Save the state for all non transient components found on the view. This 
> include save the tree structure too, so it can be reconstructed later.
> In jsf 2.0 it happens something similar. ViewDeclarationLanguage and 
> TagHandlerDelegate abstract classes were created, so some code was 
> "relocated". To be clear, the algorithm in jsf 2.0 without partial state 
> saving is this:
>  First Request:
>  - There is a call to ViewDeclarationLanguage.buildView from 
> RenderResponseExecutor.execute that cause the UIViewRoot instance to be 
> filled for first time calling to f.apply().
>  - The view is rendered.
>  - Save the state for all non transient components found on the view. This 
> include save the tree structure too, so it can be reconstructed later.
>  Postback (The user send his name and do a submit):
>  - Restore the tree structure and component state for all saved components.
>  - All lifecycle phases continues until before renderView
>  - On RenderResponseExecutor.execute there is a call to buildView, and this 
> one causes all transient components like facelets UIInstructions to be added 
> to the tree. ComponentHandler first try to detect if the component is on the 
> view before create it, and if that is true do not create it, instead it takes 
> this instance and continue apply the taghandles and it remove and add it from 
> tree, to give the chance to other transient components to be created and 
> added correctly.
>  - The view is rendered.
>  - Save the state for all non transient components found on the view. This 
> include save the tree structure too, so it can be reconstructed later.
> The algorithm in jsf 2.0 with partial state saving is different:
>  
>  First Request:
>  - There is a call to ViewDeclarationLanguage.buildView from 
> RenderResponseExecutor.execute that cause the UIViewRoot instance to be 
> filled for first time calling to f.apply().
>  - The view is rendered.
>  - Save the state for all non transient components found on the view that has 
> delta or was added after build. This does not include the structure, because 
> all state is just saved on a big Map<String,Object> where the keys are the 
> clientId for each component.
>  Postback (The user send his name and do a submit):
>  - StateManagementStrategy.restoreView calls 
> ViewDeclarationLanguage.buildView passing an empty UIViewRoot to be filled 
> for first time calling to f.apply().
>  - Restore the component state for all saved components. Note the tree 
> structure now is already provided.
>  - All lifecycle phases continues until before renderView
>  - On RenderResponseExecutor.execute there is a call to buildView, but this 
> time, since the view is already filled, nothing happens. It just return.
>  - The view is rendered.
>  - Save the state for all non transient components found on the view that has 
> delta or was added after build. This does not include the structure, because 
> all state is just saved on a big Map<String,Object> where the keys are the 
> clientId for each component.
> Really from a point of view, partial state saving integrates a param called 
> FaceletViewHandler.buildBeforeRestore with other ideas found on Trinidad.
> Now suppose this scenario:
>   Component impl:
> <composite:interface>
> </composite:interface>
> <composite:implementation>
>     <h:outputText value=" Hello " />
>     <p> 
>     <composite:insertChildren/>
>     </p> 
>     <h:outputText value=" Bye " />
> </composite:implementation>
>   Snippet on page:
>     <testComposite:simpleInsertChildren id="sic">
>       Mr. 
>       <h:inputText id="name" value="John " />
>       Smith
>     </testComposite:simpleInsertChildren>
> composite:insertChildren and composite:insertFacet uses a listener attached 
> to PostAddToViewEvent to relocate the components. So, when the listener is 
> called the body of testComposite:simpleInsertChildren looks like this:
>     <h:outputText value=" Hello " />
>     <p> 
>       Mr. 
>       <h:inputText id="name" value="John " />
>       Smith
>     </p> 
>     <h:outputText value=" Bye " />
> The reason why we do that on a listener is described on MYFACES-2317. In few 
> words, we need the relocation occur from root to branches to allow nesting on 
> composite components.
> Now the big question: Why the state is lost for child or facet components 
> using composite:insertChildren or composite:insertFacet?
> When the component tree is updated (call to buildView on render response 
> phase and postback), since the components inside the composite component were 
> moved to some location "inside" the composite component, the current 
> algorithm is unable to detect this condition, so the components are created 
> again. The reason why there is no duplicate components in the view is 
> facelets has an algoritm to mark and delete component instances that are not 
> traversed (this is the real magic behind c:if tag). Then facelets detach and 
> attach all components while traversing, triggering PostAddToViewEvent and our 
> relocation listener is activated again (before do this the components on the 
> location were deleted). 
> Why this does not happens when partial state saving is used? In this case 
> there is no "update" call. buildView just return because the view is already 
> filled.
>  In this case, c:if will not work as in jsf 1.2 if it depends on a value that 
> changes on invoke application phase (MYFACES-2483).
> In this case we have two options:
> 1. Do some algorithm that helps facelets to "jump" to the correct location 
> where the component was relocated. Then on the listener, relocate the 
> components created (UIInstructions instances) based on the component order 
> saved on first time.
>   
>   Advantages: Keep things simple and only has to deal with the components 
> that were moved in relocation.
>   Disadvantages : c:if will not work correctly inside a composite component 
> that uses composite:insertChildren or composite:insertFacet.
> 2. Let facelets create the components and then on the listener "unify the 
> tree", adding the new components and remove the ones that shouldn't be there.
>   Advantages: c:if will work correctly on all situations.
>   Disadvantages: The algorithm necessary has to traverse the tree for all 
> children and facets looking for added components.
> I tried both solutions and finally I think solution 1 is the option to do. 
> Obviously, things like this:
>     <testComposite:simpleInsertChildren id="sic">
>       Mr. 
>          <c:if test="#{helloWorldIfBean.toogle}">
>              <p>Pepito</p>
>              <h:outputText value="Perez"/>
>          </c:if>
>       Smith
>     </testComposite:simpleInsertChildren>
> will not work correctly, but if you put this one like this:
>     <testComposite:simpleInsertChildren id="sic">
>      
>       Mr. 
>          <h:panelGroup>
>            <c:if test="#{helloWorldIfBean.toogle}">
>              <p>Pepito</p>
>              <h:outputText value="Perez"/>
>            </c:if>
>          </h:panelGroup>
>       Smith
>     </testComposite:simpleInsertChildren>
> It will.
> If no objections I'll commit the solution 1 soon.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to