Package org.apache.tapestry5.corelib.components

## Class Loop<T>

- java.lang.Object
- - org.apache.tapestry5.corelib.components.Loop<T>

-     @SupportsInformalParameters
      @Events("synchronizeValues")
      public class Loop<T>
      extends java.lang.Object

  A basic looping component; loops over a number of items (provided by its source parameter), rendering its body for each one. When a Loop is inside a `Form`, it records quite a bit of state into the Form to coordinate access to the same (or equivalent) objects during the form submission as during the render. This is controlled by the formState parameter (of type `LoopFormState`) and can be 'none' (nothing stored into the form), 'values' (which stores the individual values looped over, or via a `ValueEncoder`, just the value's ids), and 'iteration' (which just stores indexes to the values within the source parameter, which means that the source parameter will be accessed during the form submission). For a non-volatile Loop inside the form, the Loop stores a series of commands that start and end heartbeats, and stores state for each value in the source parameter (either as full objects when the encoder parameter is not bound, or as client-side objects when there is an encoder). For a Loop that doesn't need to be aware of the enclosing Form (if any), the formState parameter should be bound to 'none'. When the Loop is used inside a Form, it will generate an `EventConstants.SYNCHRONIZE_VALUES` event to inform its container what values were submitted and in what order; this can allow the container to pre-load the values in a single batch form external storage, if that is appropriate.

  Component Parameters
  Name | Type | Flags | Default | Default Prefix
  --- | --- | --- | --- | ---
  element | String |  |  | literal
  The element to render. If not null, then the loop will render the indicated element around its body (on each pass through the loop). The default is derived from the component template.
  empty | org.apache.tapestry5.Block |  |  | literal
  A Block to render instead of the loop when the source is empty. The default is to render nothing.
  encoder | org.apache.tapestry5.ValueEncoder |  |  | prop
  A ValueEncoder used to convert server-side objects (provided by the "value" parameter) into unique client-side strings (typically IDs) and back. In general, when using a non-volatile Loop in a Form, you should either provide a ValueEncoder with the encoder parameter or use a "value" type for which Tapestry is configured to provide a ValueEncoder automatically. Otherwise Tapestry must fall back to using the plain index of each loop iteration, rather than the ValueEncoder-provided unique ID, for recording state into the form.
  formState | org.apache.tapestry5.corelib.LoopFormState | Not Null |  | literal
  Controls what information, if any, is encoded into an enclosing Form. The default value is org.apache.tapestry5.corelib.LoopFormState#VALUES. This parameter is only used if the component is enclosed by a Form.
  index | int |  |  | prop
  The index into the source items.
  source | Iterable | Required |  | prop
  Defines the collection of values for the loop to iterate over. If not specified, defaults to a property of the container whose name matches the Loop cmponent's id.
  value | T |  |  | prop
  The current value, set before the component renders its body.

  Component Events
  Name | Description
  --- | ---
  synchronizeValues |

  ### Basic Example

    This example is based around a NavBar component that generates a set of links to other pages in the applilcation.

    ### NavBar.tml

        <table class="navigation" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">

           <tr>
                <t:loop source="pageNames" value="pageName">
                    <td class="${tabClass}">
                        <t:pagelink page="pageName">${pageName}</t:pagelink>
                    </td>
                </t:loop>
            </tr>

        </table>

    We are assuming that the NavBar component has a pageNames property (possibly a parameter). The Loop will iterate over those page names and store each into its value parameter.

    ### NavBar.java

        public class NavBar
        {
            @Parameter(defaultPrefix="literal", required=true)
            private String pages;

            @Inject
            private ComponentResources resources;

            @Property
            private String _pageName;

            public String[] getPageNames()
            {
                return pages.split(",");
            }

            public String getTabClass()
            {
                if (pageName.equalsIgnoreCase(resources.getPageName())
                    return "current";

                return null;
            }
        }

    The component converts its pages parameter into the pageNames property by splitting it at the commas. It tracks the current pageName of the loop not just to generate the links, but to calculate the CSS class of each element on the fly. This way we can give the tab corresponding to the current page a special look or highlight.

  ### Invisible Instrumentation

    We can fold together the Loop component and the element:

    ### NavBar.tml

        <table class="navigation" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">

           <tr>
                <td t:type="loop" source="pageNames" value="pageName" class="${tabClass}">
                    <t:pagelink page="pageName">${pageName}</t:pagelink>
                </td>
            </tr>

        </table>

    Using the `t:type="loop"` attribute, the other way to identify a template element as a component, allows the Loop component to render the element's tag, the on each iteration, along with informal parameters (the class attribute). This is calledinvisible instrumentation, and it is more concise and more editor/preview friendly than Tapestry's typical markup.

  ### Forms and Loops Example

    Tapestry form control element components (TextField, etc.) work inside loops. However, some additional configuration is needed to make this work efficiently.

    With no extra configuration, each value object will be serialized into the form (if you view the rendered markup, you'll see a hidden form field containing serialized data needed by Tapestry to process the form). This can become very bloated, or may not work if the objects being iterated are not serializable.

    The typical case is database driven; you are editting objects from a database and need those objects back when the form is submitted. All that should be stored on the client is the ids of those objects. Thats what the encoder parameter is for.

    ### EditOrder.tml

        <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
            <body>

                <h1>Edit Order Quantities</h1>

                <t:form>

                    <t:errors/>

                    <t:loop source="items" value="item" encoder="encoder">
                        <div class="line-item">
                            <t:label for="quantity">${item.product.name}</t:label>
                            <t:textfield t:id="quantity" value="item.quantity"/>
                        </div>
                    </t:loop>

                    <input type="submit" value="Update"/>
                </t:form>
            </body>
        </html>

    The TextField component is rendered multiple times, once for each LineItem in the Order.

    ### EditOrder.java

        public class EditOrder
        {
            @Inject
            private OrderDAO orderDAO;

            @Property
            private final ValueEncoder<LineItem> encoder = new ValueEncoder<LineItem>()
            {
                public String toClient(LineItem value) { return String.valueOf(value.getId()); }

                public LineItem toValue(String clientValue)
                {
                    long id = Long.parseLong(clientValue);

                    return orderDAO.getLineItem(id);
                }
            };

            @Persist
            private long orderId;

            @Property
            private LineItem item;

            public List<LineItem> getItems()
            {
                return orderDAO.getLineItemsForOrder(orderId);
            }
        }

    Here, we expect the OrderDAO service to do most of the work, and we create a wrapper around it, in the form of the ValueEncoder instance.

    We've glossed over a few issues here, including how to handle the case that a particular item has been deleted or changed between the render request and the form submission, as well as how the orderId property gets set in the first place.

- - ### Constructor Summary

    Constructors
    Constructor | Description
    --- | ---
    `Loop()` |

  - ### Method Summary

    All Methods Instance Methods Concrete Methods
    Modifier and Type | Method | Description
    --- | --- | ---
    `int` | `getIndex()` |
    `T` | `getValue()` |

    - ### Methods inherited from class java.lang.Object

      `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Constructor Detail

    - #### Loop

          public Loop()

  - ### Method Detail

    - #### getIndex

          public int getIndex()

    - #### getValue

          public T getValue()