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:

    public static void contributeBindingSource(
            MappedConfiguration<String, BindingFactory> configuration,
            BindingSource bindingSource
            )
    {
        configuration.add("cycle",new CycleBindingFactory(bindingSource));
    }    

And you'll need the implementation:

import java.util.ArrayList;
import java.util.List;

import org.apache.tapestry5.Binding;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.ioc.Location;
import org.apache.tapestry5.services.BindingFactory;
import org.apache.tapestry5.services.BindingSource;

/**
 * Implementation of the cycle: binding prefix -- we parse list of bindings
 * and generate delegate bindings for each element<br>
 * default binding is literal, other bindings can be used by specifying prefix.<br>
 * example: "cycle:prop:name,prop:lastName,sth,sth else"
 */
public class CycleBindingFactory implements BindingFactory {
    private final BindingSource _bindingSource;

    public CycleBindingFactory(BindingSource source){
        this._bindingSource = source;
    }
    
    public Binding newBinding(String description, ComponentResources container, ComponentResources component,
            String expression, Location location)
    {
        List<Binding> delegates = new ArrayList<Binding>();
        String[] bindingNames = expression.split(",");

        for (String bindingName : bindingNames){
            String defaultBinding = BindingConstants.LITERAL;

            Binding binding = _bindingSource.newBinding(description, container, component, defaultBinding, bindingName, location);
            delegates.add(binding);
        }
        
        CycleBinding cycleBinding = new CycleBinding(delegates);
        container.addPageLifecycleListener(cycleBinding);
            
        return cycleBinding;
    }
}

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

import java.util.List;

import org.apache.tapestry5.Binding;
import org.apache.tapestry5.internal.bindings.AbstractBinding;
import org.apache.tapestry5.runtime.PageLifecycleListener;


public class CycleBinding extends AbstractBinding implements PageLifecycleListener{
    private final List<Binding> delegates;
    private ThreadLocal<Integer> index;

    public CycleBinding(List<Binding> delegates) {
        this.delegates = delegates;
    }

    public Object get() {
        Object ret = delegates.get(getIndex()).get();
        incrIndex();
        return ret;
    }
    
    @Override
    public boolean isInvariant() {
        return false;
    }
    
    @Override
    public Class<Object> getBindingType() {
        return Object.class;
    }

    public void containingPageDidDetach() {
        index.remove();
    }

    public void containingPageDidAttach() {
        index = createThreadLocal();
    }

    public void containingPageDidLoad() {}

    public void restoreStateBeforePageAttach() {}

    private ThreadLocal<Integer> createThreadLocal() {
        return new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return Integer.valueOf(0);
            };
        };
    }

    private int getIndex() {
        return index.get().intValue();
    }

    private void incrIndex() {
        int i = index.get().intValue() + 1;
        if (i >= delegates.size()) {
            i = 0;
        }
        index.set(Integer.valueOf(i));
    }
}

  • No labels