[ 
https://issues.apache.org/jira/browse/TRINIDAD-1940?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Andrew Robinson resolved TRINIDAD-1940.
---------------------------------------

    Resolution: Fixed
    
> Problems with the tr:forEach
> ----------------------------
>
>                 Key: TRINIDAD-1940
>                 URL: https://issues.apache.org/jira/browse/TRINIDAD-1940
>             Project: MyFaces Trinidad
>          Issue Type: Bug
>          Components: Components
>    Affects Versions: 2.0.0-beta-1
>            Reporter: Andrew Robinson
>            Assignee: Andrew Robinson
>             Fix For: 2.1.0-core
>
>
> The tr:forEach tag has issues when trying to use component references and 
> trying to keep the component state in sync with the items in a list. It also 
> does not support Maps like the c:forEach tag does.
> I seek to improve the forEach tag in Trinidad, and propose that JSF/JSTL does 
> something similar so that user's can really use the for each tag without 
> issues. Some of the for each problems are described in my blog:
> http://drewdev.blogspot.com/2008/08/cforeach-with-jsf-could-ruin-your-day.html
> I propose to address each of the issues with the JSP tag.
> First, reliable component references:
> <tr:forEach var="item" items="#{myBean.items}">
>   <tr:panelGroupLayout id="pgl1" layout="horizontal">
>     <tr:inputText id="it1" label="Enter value:" value="#{item.text}" 
> autoSubmit="true" />
>     <tr:outputText id="ot1" value="Value is: #{item.text}" 
> partialTriggers="it1" />
>   </tr:panelGroupLayout>
> </tr:forEach>
> The problem is that the author intended the output text component to 
> partially updated by the sibling input text when it changed value, but that 
> is not the result. Instead, the partial triggers is always "it1", but the 
> generated input text components are "ot1", "ot1j_id_1" and "ot1j_id_2". The 
> result is that the output text components all partially update only when the 
> first input text is changed.
> Another problem is that the indexed value expressions and the var status map 
> that is put into the variable mapper by the for each loop is static.  Take 
> the following example:
> <tr:panelGroupLayout id="pgl1" layout="scroll">
>   <tr:forEach var="item" items="#{myBean.items}" varStatus="vs">
>     <tr:outputText id="ot1" value="This is the last item in the list: 
> #{vs.last}. Item #{item.text}." />
>   </tr:forEach>
> </tr:panelGroupLayout>
> Say during the first pass, the items in the list are A, B and C and the page 
> would look like this:
> This is the last item in the list: false. Item A.
> This is the last item in the list: false. Item B.
> This is the last item in the list: true. Item C.
> Now, consider what the output would be if someone added an object D to the 
> end of the list during invoke application:
> This is the last item in the list: false. Item A.
> This is the last item in the list: false. Item B.
> This is the last item in the list: true. Item C.
> This is the last item in the list: true. Item D.
> Notice that both C and D think they are the last. The reason is that the 
> UIComponentClassicTagBase will find the components generated for A, B and C 
> during the findComponent call. It will only create a new component for D. 
> When D is created, it will pick up the new variable status map created by the 
> for each tag in its value expressions. Because when three earlier components 
> were build, C was the last item, and the variable status map in their value 
> expressions did not reflect any changes. D gets the correct values since it 
> was just created.
> -----------------
> I propose to reduce any work required by page developers to implement the for 
> each loop with re-ordering support (avoid having to use immediate EL in the 
> ID attribute) and fix the issues above.
> The proposal is that Trinidad Tag based components (those components created 
> by UIXComponentELTag) will be able to have key-based IDs automatically 
> generated as a result of being in a for each tag. So consider this example:
> <tr:forEach var="item" items="#{bean.items}" varStatus="vs">
>   <tr:inputText id="it1" value="#{item.value}" partialSubmit="true" />
>   <tr:outputText id="ot1" value="Value is: #{item.value}"
>     partialTriggers="it1_${vs.key}" />
> </tr:forEach>
> In this code, the IDs of the components would automatically pick up the item 
> key. The item key would be stored on the var status. For List this key would 
> simply be the index, for Map it would be the map key and CollectionModel 
> would use the row key. UIXComponentELTag could check to see if a parent tag 
> desires to alter the component IDs, which in this case, the for each would. 
> With that set, the tags would alter the component IDs to append the key. For 
> example, "_" + key would be appended to each component ID (it1 would become 
> it1_A if the key were "A").
> The for each tag would set the key into the variable mapper so that, instead 
> of indexes, the key would be used to evaluate the EL with the limitation that 
> the key would be the index for List, in which the current behavior would be 
> retained of the component state staying with the index rather than with the 
> object.
> Due to the fact that the JSP does not perform any component reordering, the 
> org.apache.myfaces.trinidad.change.ReorderChildrenComponentChange component 
> change class can be use to alter the component order and also notify the 
> component change manager of the reordering. This would require users that 
> wish to reorder the items in the list to ensure to both re-order the list as 
> well as reorder the components by applying a component change.
> Changes to UIXComponentELTag
> Due to the fact that UIComponentClassicTagBase is a JSF class that we cannot 
> modify, in order to fix the tr:forEach bugs, we need to work with what we can 
> change. We will want to follow this up with proposed changes to the JSF and 
> potentially the JSTL specifications to be able to make this type of 
> functionality standard.
> Since UIXComponentELTag extends UIComponentClassicTagBase, it can call 
> protected methods to alter the ID of the tag. With the altering of the tag 
> ID, the UIComponentClassicTagBase will produce different component IDs. In 
> order to pull this off, there needs to be a defined contract between the for 
> each tag and the Trinidad component tags. This contract could be done with 
> static methods on UIXComponentELTag:
> Proposed methods to be added to UIXComponentELTag
> /**
>  * Push a component ID suffix onto the page context to append to component 
> IDs generated by {@link UIXComponentELTag}.
>  * This will append the suffix on all components up to a and including the 
> text naming container.
>  * @param pageContext the JSP page context
>  * @param suffix the suffix to append to component IDs
>  */
> public static void pushComponentIdSuffix(PageContext pageContext, String 
> suffix)
> {
>   ...
> }
> /**
>  * Pop the last component ID suffix added to the page context.
>  * @see #pushComponentIdSuffix(PageContext, String)
>  * @param pageContext the JSP page context
>  */
> public static void popComponentIdSuffix(PageContext pageContext)
> {
>   ...
> }
> The ForEachTag would then call these methods before and after the body 
> processing respectively. The idea would be that if a UIXComponentELTag 
> created a naming container, it would clear any suffixes for children of the 
> naming container. This is because the IDs would already be unique in the 
> naming container, and there would be no reason to modify them, and it would 
> also stop these changes from affecting any IDs inside of included pages. The 
> push/pop method would be used to support a stack usage. This way, if nested 
> for each tags are present, multiple suffixes would be added to ensure that 
> the IDs in one for each tag would conflict with another's.
> As this would be a documented behavior, users could leverage this API to 
> ensure that their component references (like the partialTriggers attribute) 
> point to the correct component.
> Changes to ForEachTag
> The for each tag, with this proposal, would need to be modified to call the 
> new methods of UIXComponentELTag in doStartTag and doAfterBody functions.
> The tag will need to change to provide dynamic value expressions for the var 
> status and the var so that changes to the items will not break EL. This would 
> involve using the key as a reference instead of an index for non list based 
> items. The var status would need point to a component tree based attribute 
> that could be updated in each request. For example, this would allow new 
> items to be added, and the last attribute to correctly reflect that new state.
> The for each tag would be change to support Map and CollectionModel as well, 
> and adding the key as a valid attribute on the varStatus object.
> Optionally skip this for List and array
> If we did not implement the ID suffixing for List (and arrays), existing 
> applications would not be affected. This is one consideration to have to 
> ensure that backwards compatibility is maintained. Another alternative is to 
> use a web.xml configuration parameter and only introduce the ID suffixing 
> when the web XML parameter has enabled it for lists and arrays.

--
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