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 }