Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for 
change notification.

The following page has been changed by MarceloLotif:
http://wiki.apache.org/tapestry/Tapestry5SelectObject

The comment on the change is:
added the SelectObject component based on the DanielJue's GenericSelectionModel 

New page:
== SelectObject Component ==

This is an alternative implementation of the Select core component that allows 
you to generate a drop down list based on a List<Object> attribute and 
retrieves the whole object as the result of the selection.

See also :
 * 
[http://www.phy6.net/wiki/tiki-index.php?page=Tapestry+5+GenericSelectionModel 
Tapestry5 How to use the SELECT component]
 * ["Tapestry5DisplayableSelectionModel"]

First, you have to add a dependency to your pom.xml. This will add the Apache 
BeanUtils to your project:
{{{
<dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils-core</artifactId>
   <version>1.7.0</version>
</dependency>
}}}

== GenericSelectionModel.java ==
This is the model that the component will use to render your List. Add it to 
any package visible to the components package.

{{{
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.tapestry.OptionGroupModel;
import org.apache.tapestry.OptionModel;
import org.apache.tapestry.internal.OptionModelImpl;
import org.apache.tapestry.util.AbstractSelectModel;

/**
 * @author jued
 * 
 * @param <T>
 */
public class GenericSelectionModel<T> extends AbstractSelectModel {

        private String labelField;

        private List<T> list;

        public GenericSelectionModel(List<T> list, String labelField) {
                this.labelField = labelField;
                this.list = list;
        }

        public List<OptionGroupModel> getOptionGroups() {
                return null;
        }

        public List<OptionModel> getOptions() {
                List<OptionModel> optionModelList = new 
ArrayList<OptionModel>();
                try {
                        for (T obj : list) {
                                if (labelField == null) {
                                        optionModelList.add(new 
OptionModelImpl(obj + "", false, obj, new String[0]));
                                } else {
                                        optionModelList.add(new OptionModelImpl(
                                                        
BeanUtils.getProperty(obj, labelField), false, obj, new String[0]));
                                }
                        }
                } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (NoSuchMethodException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                return optionModelList;
        }
}
}}}

== GenericValueEncoder.java ==
This is the encoder that the component will use to get your selection. Add it 
to the same package of the GenericSelectionModel.
{{{
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.tapestry.ValueEncoder;

public class GenericValueEncoder<T> implements ValueEncoder<T> {

        private String labelField;

        private List<T> list;

        public GenericValueEncoder(List<T> list, String labelField) {
                this.list = list;
                this.labelField = labelField;
        }

        public String toClient(T obj) {
                try {
                        if (labelField == null) {
                                return obj + "";
                        } else {
                                return BeanUtils.getProperty(obj, labelField);
                        }
                } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (NoSuchMethodException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                return null;
        }

        public T toValue(String string) {
                try {
                        for (T obj : list) {
                                if (labelField == null) {
                                        if ((obj + "").equals(string)) {
                                                return obj;
                                        }
                                } else {
                                        if (BeanUtils.getProperty(obj, 
labelField).equals(string)) {
                                                return obj;
                                        }
                                }
                        }
                } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (NoSuchMethodException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                return null;
        }
}
}}}

== SelectObject.java ==
This is the Select (org.apache.tapestry.corelib.components) with some simple 
modifications to use those models above and ommit part of the custom parameters.
{{{

import org.apache.tapestry.Binding;
import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.FieldValidator;
import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.OptionModel;
import org.apache.tapestry.SelectModel;
import org.apache.tapestry.SelectModelVisitor;
import org.apache.tapestry.ValidationException;
import org.apache.tapestry.ValidationTracker;
import org.apache.tapestry.ValueEncoder;
import org.apache.tapestry.annotations.BeforeRenderTemplate;
import org.apache.tapestry.annotations.Environmental;
import org.apache.tapestry.annotations.Inject;
import org.apache.tapestry.annotations.Parameter;
import org.apache.tapestry.corelib.base.AbstractField;
import org.apache.tapestry.internal.util.SelectModelRenderer;
import org.apache.tapestry.services.FieldValidatorDefaultSource;
import org.apache.tapestry.services.FormSupport;
import org.apache.tapestry.services.Request;
import org.apache.tapestry.services.ValueEncoderFactory;
import org.apache.tapestry.services.ValueEncoderSource;
import org.apache.tapestry.util.EnumSelectModel;
import org.exemplo.selectComponent.models.GenericSelectionModel;
import org.exemplo.selectComponent.models.GenericValueEncoder;

/**
 * Select an item from a list of values, using an [X]HTML &lt;select&gt; 
element on the client side.
 * An validation decorations will go around the entire &lt;select&gt; element.
 * <p>
 * A core part of this component is the [EMAIL PROTECTED] ValueEncoder} (the 
encoder parameter) that is used to
 * convert between server-side values and client-side strings. In many cases, a 
[EMAIL PROTECTED] ValueEncoder}
 * can be generated automatically from the type of the value parameter. The
 * [EMAIL PROTECTED] ValueEncoderSource} service provides an encoder in these 
situations; it can be overriden
 * by binding the encoder parameter, or extended by contributing a [EMAIL 
PROTECTED] ValueEncoderFactory} into
 * the service's configuration.
 */
public final class SelectObject extends AbstractField
{
    private class Renderer extends SelectModelRenderer
    {

        public Renderer(MarkupWriter writer)
        {
            super(writer, _encoder);
        }

        @Override
        protected boolean isOptionSelected(OptionModel optionModel)
        {
            Object value = optionModel.getValue();

            return value == _value || (value != null && value.equals(_value));
        }
    }
    
    @Inject
    private FieldValidatorDefaultSource _fieldValidatorDefaultSource;

    @Inject
    private Locale _locale;

    @Parameter(required = true)
    private List<Object> _list;
    
    @Parameter
    private String _labelField = null;
    
    // Maybe this should default to property "<componentId>Model"?
    /**
     * The model used to identify the option groups and options to be presented 
to the user. This
     * can be generated automatically for Enum types.
     */
//    @Parameter(required = true)
//    private SelectModel _model;
    
    private GenericSelectionModel<Object> _model;

    /**
     * Allows a specific implementation of [EMAIL PROTECTED] ValueEncoder} to 
be supplied. This is used to
     * create client-side string values for the different options.
     * 
     * @see ValueEncoderSource
     */
//    @Parameter
//    private ValueEncoder _encoder;

    private GenericValueEncoder<Object> _encoder;
    
    @Inject
    private Request _request;

    @Inject
    private ComponentResources _resources;

    @Environmental
    private ValidationTracker _tracker;

    /** Performs input validation on the value supplied by the user in the form 
submission. */
    @Parameter(defaultPrefix = "validate")
    @SuppressWarnings("unchecked")
    private FieldValidator<Object> _validate = NOOP_VALIDATOR;

    /** The value to read or update. */
    @Parameter(required = true, principal = true)
    private Object _value;

    @Inject
    private ValueEncoderSource _valueEncoderSource;

    @Override
    protected void processSubmission(FormSupport formSupport, String 
elementName)
    {
        _encoder = new GenericValueEncoder<Object>(_list, _labelField);
        
        String primaryKey = _request.getParameter(elementName);
        
        Object selectedValue = _encoder.toValue(primaryKey);

        try
        {
            _validate.validate(selectedValue);

            _value = selectedValue;
        }
        catch (ValidationException ex)
        {
            _tracker.recordError(this, ex.getMessage());
            return;
        }
    }

    void afterRender(MarkupWriter writer)
    {
        writer.end();
    }

    void beginRender(MarkupWriter writer)
    {
        writer.element("select", "name", getElementName(), "id", getClientId());
        _encoder = new GenericValueEncoder<Object>(_list, _labelField);
        _model = new GenericSelectionModel<Object>(_list, _labelField);
        // Disabled, informals via mixins
    }

    @SuppressWarnings("unchecked")
    ValueEncoder defaultEncoder()
    {
        return _valueEncoderSource.createEncoder("value", _resources);
    }

    @SuppressWarnings("unchecked")
    SelectModel defaultModel()
    {
        Class valueType = _resources.getBoundType("value");

        if (valueType == null) return null;

        if (Enum.class.isAssignableFrom(valueType))
            return new EnumSelectModel(valueType, 
_resources.getContainerMessages());

        return null;
    }

    /**
     * Computes a default value for the "validate" parameter using
     * [EMAIL PROTECTED] FieldValidatorDefaultSource}.
     */
    FieldValidator defaultValidate()
    {
        Class type = _resources.getBoundType("value");

        if (type == null) return null;

        return _fieldValidatorDefaultSource.createDefaultValidator(
                this,
                _resources.getId(),
                _resources.getContainerMessages(),
                _locale,
                type,
                _resources.getAnnotationProvider("value"));
    }

    Binding defaultValue()
    {
        return createDefaultParameterBinding("value");
    }

    @BeforeRenderTemplate
    void options(MarkupWriter writer)
    {
        SelectModelVisitor renderer = new Renderer(writer);

        _model.visit(renderer);
    }

    // For testing.

    void setModel(GenericSelectionModel model)
    {
        _model = model;
    }

    void setValue(Object value)
    {
        _value = value;
    }

    void setValueEncoder(GenericValueEncoder encoder)
    {
        _encoder = encoder;
    }
}


}}}

And you're done!

== Usage ==
Using it is very simple. On your Page class, declare the attributes that the 
component will use:
{{{
public class SomePage {

   private SomeBean _someBean;
   private List<SomeBean> _beanList;

   public SomeBean getSomeBean(){
      return _someBean;
   }

   public void setSomeBean(SomeBean _someBean){
      this._someBean = _someBean;
   }
   
   public List<SomeBean> getBeanList(){
      return _beanList;
   }

   public void setBeanList(List<SomeBean> _beanList){
      this._beanList = _beanList;
   }
   
}

}}}

Then, on the html file, call the component as shown above (don't forget to 
populate the list before doing this):
{{{
<form t:type="Form">
   <t:selectObject list="beanList" value="someBean" labelField="literal:name"/>
   <t:submit/>
</form>
}}}

The '''labelField''' parameter receive the name of an atribute of the bean 
class that will be rendered as the options of the drop down list. The component 
will return to the ''_someBean'' attribute the value of the selection.

You can even have a '''List<String>''' as the list to be rendered. In this 
case, you have to ommit the ''label field'' parameter.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to