On 08 August 2005, Pablo Ruggia said:
> 2) Create some components that creates inputs programatically (i know it can 
> be done, but not how).

I have succesfully pulled off something like this.  It's not too pretty, but
it seems to work.  The core is a simple class called UserField that
describes one field of the form that edits a user:

"""
/**
 * A single field in the form for editing user properties.  A list
 * of these describes the core of any user form (account application,
 * edit my profile, etc.).
 */
public class UserField
{
    /** How values for this field are get/set on some model object */
    private String mPropertyName;

    /** Should the user be able to supply a value? */
    private boolean mVisible = true;
    
    /** Should the user be told that this field is required? */
    private boolean mDisplayRequired = false;
    
    /** Should we get Tapestry to automatically enforce that this field is
     *  required? 
     */
    private boolean mEnforceRequired = false;

    
    private int mMinimumLength = 0;
    private int mMaximumLength = 255;
    
    /** Width of the text entry field in characters; if -1, derive from 
mMaximumLength */
    private int mFieldWidth = -1;
    
    /** Map from Locale to UserFieldText instance */
    private Map mText = new HashMap();
    
    /**
     * Should the text in this field be masked as it's entered? 
     * (ie. is this a password field?) 
     */
    private boolean mMasked = false;

    
    public UserField( String propertyName )
    {
        mPropertyName = propertyName;
    }
    
    public UserField( String propertyName,
                      boolean visible,
                      boolean required,
                      int minLength,
                      int maxLength )
    {
        this( propertyName );
        mVisible = visible;
        setRequired( required );
        mMinimumLength = minLength;
        mMaximumLength = maxLength;
    }

    [...getters and setters omitted...]
"""

Problem #1: this isn't limited to editing user accounts, so calling it
UserField is kind of lame.  Problem #2: it doesn't accomodate any HTML
<input> tag other than <input type="text">.  That's fine for us right now,
but not enough for the general case.

The Tapestry end is handled by a component called CustomUserField, which
wraps a ValidField component and a UserField instance and does all the magic
to make the UserField instance influence the ValidField component.  The
component spec looks like this:

"""
<component-specification 
    class="...CustomUserField"
    allow-body="no"
    allow-informal-parameters="no">
    
    <description>
      Generates an HTML table row containing a label, input tag, and
      field description all derived from a UserField instance.
    </description>

    <parameter name="model"
               type="java.lang.Object"
               required="yes"
               direction="in"/>
    <parameter name="field" 
               type="....UserField"
               required="yes"
               direction="in"/>

    <bean name="validator" 
          class="....UserFieldValidator">
      <set-property name="field" expression="field"/>
    </bean>

    <component id="validField" type="ValidField">
      <static-binding name="displayName" 
value="will-be-overridden-in-component-class"/>
      <binding name="validator" expression="beans.validator"/>
      <binding name="value" expression="fieldValue"/>
      <binding name="size" expression="field.fieldWidth"/>
      <binding name="maxlength" expression="field.maximumLength"/>
      <binding name="hidden" expression="field.masked"/>
    </component>
</component-specification>
"""

Then the fun stuff is in the component class: the render() method uses
getField() to fetch a UserField instance, which then drives how the
component is rendered.  E.g. if getField().isRequired(), then print the
string "(required)" with a particular CSS class right after the <input> tag.
(Oh yeah, another simplification: each CustomUserField assumes that it's one
row of a two-column table where column 1 is the field label, column 2 the
field itself plus any help/error text.)  The tricky part was binding the
model object's value to the ValidField via the 'fieldValue' component
property:

""" 
class CustomUserField extends AbstractComponent
{
    ...

    public Object getFieldValue() throws OgnlException
    {
        return Ognl.getValue( parsePropertyName(), getModel() );
    }

    public void setFieldValue( Object value ) throws OgnlException
    {
        Ognl.setValue( parsePropertyName(), getModel(), value );
    }

    private Object parsePropertyName()
    {
        return OgnlUtils.getParsedExpression( getField().getPropertyName() );
    }
}
""" 

Like I said, it ain't pretty, but it seems to work.

        Greg

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

Reply via email to