this is another example like : Tapestry5HowToAddBindingPrefix

This binding prefix will return different value for each call, and we will use it for zebra effect on grid or any other loop.

First let's see usage example:

<t:grid ....  rowClass="cycle:line1,line2">
...
</t:grid>
or any loop
<t:loop ...>
<div class="${cycle:line1,line2}">aaaa</div>
</tloop>

but not separately:

<div class="${cycle:line1,line2}">aaaa</div>
<div class="${cycle:line1,line2}">aaaa</div>
<div class="${cycle:line1,line2}">aaaa</div>

that is it, no need for counter, and counter % 2 and stuff..

To make it work you have to tell tapestry about the "cycle:" binding prefix. So, add following to your module:

   1     public static void contributeBindingSource(
   2             MappedConfiguration<String, BindingFactory> configuration,
   3             BindingSource bindingSource
   4             )
   5     {
   6         configuration.add("cycle",new CycleBindingFactory(bindingSource));
   7     }    

And you'll need the implementation:

   1 import java.util.ArrayList;
   2 import java.util.List;
   3 
   4 import org.apache.tapestry5.Binding;
   5 import org.apache.tapestry5.BindingConstants;
   6 import org.apache.tapestry5.ComponentResources;
   7 import org.apache.tapestry5.ioc.Location;
   8 import org.apache.tapestry5.services.BindingFactory;
   9 import org.apache.tapestry5.services.BindingSource;
  10 
  11 /**
  12  * Implementation of the cycle: binding prefix -- we parse list of bindings
  13  * and generate delegate bindings for each element<br>
  14  * default binding is literal, other bindings can be used by specifying prefix.<br>
  15  * example: "cycle:prop:name,prop:lastName,sth,sth else"
  16  */
  17 public class CycleBindingFactory implements BindingFactory {
  18     private final BindingSource _bindingSource;
  19 
  20     public CycleBindingFactory(BindingSource source){
  21         this._bindingSource = source;
  22     }
  23     
  24     public Binding newBinding(String description, ComponentResources container, ComponentResources component,
  25             String expression, Location location)
  26     {
  27         List<Binding> delegates = new ArrayList<Binding>();
  28         String[] bindingNames = expression.split(",");
  29 
  30         for (String bindingName : bindingNames){
  31             String defaultBinding = BindingConstants.LITERAL;
  32 
  33             Binding binding = _bindingSource.newBinding(description, container, component, defaultBinding, bindingName, location);
  34             delegates.add(binding);
  35         }
  36         
  37         CycleBinding cycleBinding = new CycleBinding(delegates);
  38         container.addPageLifecycleListener(cycleBinding);
  39             
  40         return cycleBinding;
  41     }
  42 }

And the binding itself:

Note: that starting with Tapestry 5.2 PageBindings do not appear to be thread safe when running without page pools. So now we store the index in a ThreadLocal which works around this upgrade issue. (The previous version simply used an int for the index instead of a ThreadLocal).

   1 import java.util.List;
   2 
   3 import org.apache.tapestry5.Binding;
   4 import org.apache.tapestry5.internal.bindings.AbstractBinding;
   5 import org.apache.tapestry5.runtime.PageLifecycleListener;
   6 
   7 
   8 public class CycleBinding extends AbstractBinding implements PageLifecycleListener{
   9     private final List<Binding> delegates;
  10     private ThreadLocal<Integer> index;
  11 
  12     public CycleBinding(List<Binding> delegates) {
  13         this.delegates = delegates;
  14     }
  15 
  16     public Object get() {
  17         Object ret = delegates.get(getIndex()).get();
  18         incrIndex();
  19         return ret;
  20     }
  21     
  22     @Override
  23     public boolean isInvariant() {
  24         return false;
  25     }
  26     
  27     @Override
  28     public Class<Object> getBindingType() {
  29         return Object.class;
  30     }
  31 
  32     public void containingPageDidDetach() {
  33         index.remove();
  34     }
  35 
  36     public void containingPageDidAttach() {
  37         index = createThreadLocal();
  38     }
  39 
  40     public void containingPageDidLoad() {}
  41 
  42     public void restoreStateBeforePageAttach() {}
  43 
  44     private ThreadLocal<Integer> createThreadLocal() {
  45         return new ThreadLocal<Integer>() {
  46             @Override
  47             protected Integer initialValue() {
  48                 return Integer.valueOf(0);
  49             };
  50         };
  51     }
  52 
  53     private int getIndex() {
  54         return index.get().intValue();
  55     }
  56 
  57     private void incrIndex() {
  58         int i = index.get().intValue() + 1;
  59         if (i >= delegates.size()) {
  60             i = 0;
  61         }
  62         index.set(Integer.valueOf(i));
  63     }
  64 }

Tapestry5HowToAddBindingPrefixCycle (last edited 2011-04-09 00:28:49 by DavidRees)