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 fanf:
http://wiki.apache.org/tapestry/Tapestry5HowToCreateAPropertyEditBlock

New page:
= How to create and contribute Property Edit Block =

This little documentation shows how to create a custom Property editor for the 
[http://tapestry.apache.org/tapestry5/tapestry-core/guide/beaneditform.html 
BeanEditForm] component.

We will follow the tutorial available on 
[http://tapestry.apache.org/tapestry5/tapestry-core/guide/beaneditform.html 
Tapestry 5 BeanEditForm].

Our editor will be functionally equivalent to the Enum editor (a dropdown 
select), but we want to manage a list of value as source of option for the drop 
down list in place of an Enum.

In the following part of this doc, ${Tapestry5 java root} represents the 
tapestry root package (the one configured in your web.xml) and ${Tapestry5 
resources root} represents the matching resources package.

== Process to define a new block editor ==

So, the first thing to define is the object that will represent the list of 
value with a selection.

This class is quite simple: it contains a list of available options (Strng) and 
an index that point to the selected value. We put it in a data package, apart 
from Tapestry monitored package.

When initialized, we just need to change the index to use it.

A more generic set of class to create drop down select from object is available 
[http://www.phy6.net/wiki/tiki-index.php?page=Tapestry+5+GenericSelectionModel 
here]

{{{
public class DropDownList {
        private List<String> options;
        private int selected;
        /**
         * Retrieve the available options. The returned list is
         * an unmodifiable view of internal representation
         * of options.
         * @return unmodifiable list of options
         */
        public List<String> getOptions() {...}

        /**
         * Return the index of the currently selected
         * option, or -1 if none selected.
         * @return the index
         */
        public int getSelected() {...}

        /**
         * Set the list of available options to "options".
         * The list can't be null. The sorting order is
         * preserved, and the selected index is reseted to
         * -1
         * @param List<String> a non null list of options
         */
        public void setOptions(List<String> options) {...}

        /**
         * Set the selected option to corresponding index.
         * The parameter must be in the range of options.
         * @param selected
         */
        public void setSelected(int selected) {...}

        /**
         * Return the currently selected option if exists,
         * null otherwise.
         * @return the selected option
         */
        public String getOption() {...}
}
}}}
The complete code for that class is available 
[http://svn.forge.objectweb.org/cgi-bin/viewcvs.cgi/interldap/interldap-wui-t5/trunk/src/main/java/org/interldap/wui/tapestry/data/DropDownList.java?view=log&rev=127
  here]

We have to specify a name corresponding to our class to the 
`DefaultDataTypeAnalyzer` of tapestry 5 in `${Tapestry5 java 
root}/services/AppModule.java`:

{{{
public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, 
String> configuration) {
    configuration.add(DropDownList.class, "dropdown");
}
}}}

([http://svn.forge.objectweb.org/cgi-bin/viewcvs.cgi/interldap/interldap-wui-t5/trunk/src/main/java/org/interldap/wui/tapestry/services/AppModule.java?view=log&rev=127
  here is the code of AppModule.java])

Now that the easy part is done, we have to define the block that will be in 
charge to transform the `DropDownList` to a select input.

The block has to be defined in the page assigned to property editor block 
contributions (named `${tapestry5 resources 
root}/services/AppPropertyEditBlocks.html` as in the the tutorial):
{{{
<div xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
        <t:block t:id="dropdown">
                <t:label for="select"/>
                <input t:id="select"/>
        </t:block>
</div>
}}}

([http://svn.forge.objectweb.org/cgi-bin/viewcvs.cgi/interldap/interldap-wui-t5/trunk/src/main/resources/org/interldap/wui/tapestry/pages/AppPropertyEditBlocks.html?view=log&rev=127
 complete code of AppPropertyEditBlocks.html])

We will explain the matching Java class (`${Tapestry5 java 
root}/services/AppPropertyEditBlocks.java`) in the next chapter, as it's the 
most interesting part :)

Finally, we contribute our new block editor to Tapestry in `${Tapestry5 java 
root}/services/AppModule.java`:
{{{
public void contributeBeanBlockSource(Configuration<BeanBlockContribution> 
configuration) {
    configuration.add(new BeanBlockContribution("dropdown", 
"AppPropertyEditBlocks", "dropdown", true));
}
}}}

== Details of `AppPropertyEditBlocks.java` ==

Now that the global infrastructure is in place, we have to deal with the logic 
of the editor in `AppPropertyEditBlocks.java`.
Basically, we have two things to deals with:
 * how the parameters are passed to the property editor,
 * and how we implement a drop-down select component in tapestry.
These concerns are addressed with that code:
{{{
@Environmental
private PropertyEditContext context;
public PropertyEditContext getContext() { return context; }

@SuppressWarnings("unused")
@Component(parameters =  { "value=selected",  "encoder=valueEncoderForSelected",
         "validate=prop:context.validator","label=prop:context.label",
          "model=selectModelForDropDownList", 
“clientId=prop:context.propertyId" })
private Select select;
}}}

The environmental `PropertyEditContext` is the object "pushed in the context" 
of the block editor by the `BeanEditForm` for each properties of the edited 
bean. It is that object that is used as data source for the editor. So, for us, 
it will be a `DropDownList` object, (for this example, we don't really care own 
the bean editor push and pop it to environment, but you can understand it in 
the 
[http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditForm.java
 BeanEditForm code], just search for `_environment.push()` / 
`_environment.pop()` ).

A drop-down list is implemented by the 
[http://tapestry.apache.org/tapestry5/tapestry-core/component-parameters.html#org.apache.tapestry.corelib.components.select
 select component] in tapestry 5.
This component is built with a '''model''' that is the source of available 
options, a '''value''' that is the bi-directional conduit to get/set the 
selected value, and a '''value encoder''' that translate the value in a 
displayable shape (a String), and reciprocally (String to value type).

So, we let '''validate''', '''label''' and '''clientId''' parameters to what 
the `BeanEditForm` put into the context (that's why these parameters begin by 
'''prop:contex''' ;) and we concentrate to '''value''', '''encoder''' and 
'''model'''.

=== The value and the `ValueEncoder` ===

With the way `DropDownList` works, for us the value is the index of the 
selected option (the ''selected'' property). It is this property that will be 
updated on a form submit.

So we provide a getter/setter for this property, knowing that all we have is 
the  `PropertyEditContext` passed by the environment:
{{{
public int getSelected() {
    return ((DropDownList)this.context.getPropertyValue()).getSelected();
}
public void setSelected(int value) {
    ((DropDownList)this.context.getPropertyValue()).setSelected(value);
}
}}}

Our value encoder has to translate Integer to String, it's not to hard to 
define:

{{{
public ValueEncoder getValueEncoderForSelected() {
    return new ValueEncoder() {
        public String toClient(Object value) { return 
((Integer)value).toString();}
        public Object toValue(String clientValue) { return new 
Integer(clientValue);}
    };
}
}}}

With that, the form is able to update the selected option.

=== The model ===

Select component need a model that provides options. We provide a really 
minimalist implementation that transform a list of strings to a model (again, a 
more generic model is exposed 
[http://www.phy6.net/wiki/tiki-index.php?page=Tapestry+5+GenericSelectionModel 
here]).

{{{
public class ValueSelectModel extends AbstractSelectModel {
    /* the list of options */
    private List<OptionModel> optionModels;

    /* we just want to create model from list of string... */
    public ValueSelectModel(DropDownList dropDownString) {
        optionModels = new ArrayList<OptionModel>();
        List<String> options = dropDownString.getOptions();
        for (int i = 0; i < options.size(); i++){
           optionModels.add(new ValueOptionModel(new Integer(i), 
options.get(i),false,null));
        }
    }

    /* we don't use that... */
    public List<OptionGroupModel> getOptionGroups() {return null;}
    /* retrieve the list of options */
    public List<OptionModel> getOptions() { return this.optionModels; }

    /* we have to define what is an option, so we must implement OptionModel */
    private class ValueOptionModel implements OptionModel {
        [...]

        public ValueOptionModel(Object value, String label, boolean disabled, 
Map<String, String> attrs) {...}

        public Map<String, String> getAttributes() {...}
        public String getLabel() {...}
        public Object getValue() {...}
        public boolean isDisabled() {...}
    } /* end of class ValueOptionModel */
}
}}}

For an overview of the code, you can look at the complete code of 
[http://svn.forge.objectweb.org/cgi-bin/viewcvs.cgi/interldap/interldap-wui-t5/trunk/src/main/java/org/interldap/wui/tapestry/pages/
 AppPropertyEditBlocks.java?view=log&rev=127 AppPropertyEditBlocks.java].

And with all that, you should be able to use your new editor to edit class with 
`DropDownList` properties !

You may see 
[http://svn.forge.objectweb.org/cgi-bin/viewcvs.cgi/interldap/interldap-wui-t5/trunk/src/test/java/org/interldap/wui/tapestry/data/Person.java?view=log&rev=127
 a person example, with a month selection here].

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

Reply via email to