    001// Copyright 2007-2013 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.corelib.components;
    016
    017import org.apache.tapestry5.*;
    018import org.apache.tapestry5.annotations.*;
    019import org.apache.tapestry5.beaneditor.BeanModel;
    020import org.apache.tapestry5.internal.beaneditor.BeanModelUtils;
    021import org.apache.tapestry5.ioc.annotations.Inject;
    022import org.apache.tapestry5.services.BeanModelSource;
    023
    024/**
    025 * A component that creates an entire form editing the properties of a particular bean. Inspired by <a
    026 * href="http://www.trailsframework.org/">Trails</a> and <a href="http://beanform.sourceforge.net/">BeanForm</a> (both
    027 * for Tapestry 4). Generates a simple UI for editing the properties of a JavaBean, with the flavor of UI for each
    028 * property (text field, checkbox, drop down list) determined from the property type (or by other means, such as an
    029 * annotation), and the order and validation for the properties determined from annotations on the property's getter and
    030 * setter methods.
    031 * <p/>
    032 * You may add block parameters to the component; when the name matches (case insensitive) the name of a property, then
    033 * the corresponding Block is renderered, rather than any of the built in property editor blocks. This allows you to
    034 * override specific properties with your own customized UI, for cases where the default UI is insufficient, or no
    035 * built-in editor type is appropriate.
    036 * <p/>
    037 * BeanEditForm contains a {@link org.apache.tapestry5.corelib.components.Form} component and will trigger all the
    038 * events of a Form.
    039 *
    040 * @tapestrydoc
    041 * @see org.apache.tapestry5.beaneditor.BeanModel
    042 * @see org.apache.tapestry5.services.BeanModelSource
    043 * @see org.apache.tapestry5.corelib.components.PropertyEditor
    044 * @see org.apache.tapestry5.beaneditor.DataType
    045 * @see Form
    046 * @see Errors
    047 * @see BeanEditor
    048 */
    049@SupportsInformalParameters
    050@Events(EventConstants.PREPARE)
    051public class BeanEditForm implements ClientElement, FormValidationControl
    052{
    053
    054    /**
    055     * The text label for the submit button of the form, by default "Create/Update".
    056     */
    057    @Parameter(value = "message:core-submit-label", defaultPrefix = BindingConstants.LITERAL)
    058    @Property
    059    private String submitLabel;
    060
    061    /**
    062     * The object to be edited. This will be read when the component renders and updated when the form for the component
    063     * is submitted. Typically, the container will listen for a "prepare" event, in order to ensure that a non-null
    064     * value is ready to be read or updated. Often, the BeanEditForm can create the object as needed (assuming a public,
    065     * no arguments constructor). The object property defaults to a property with the same name as the component id.
    066     */
    067    @Parameter(required = true, autoconnect = true)
    068    @Property
    069    private Object object;
    070
    071    /**
    072     * A comma-separated list of property names to be retained from the
    073     * {@link org.apache.tapestry5.beaneditor.BeanModel} (only used
    074     * when a default model is created automatically).
    075     * Only these properties will be retained, and the properties will also be reordered. The names are
    076     * case-insensitive.
    077     */
    078    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    079    private String include;
    080
    081    /**
    082     * A comma-separated list of property names to be removed from the {@link org.apache.tapestry5.beaneditor.BeanModel}
    083     * (only used
    084     * when a default model is created automatically).
    085     * The names are case-insensitive.
    086     */
    087    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    088    private String exclude;
    089
    090    /**
    091     * A comma-separated list of property names indicating the order in which the properties should be presented. The
    092     * names are case insensitive. Any properties not indicated in the list will be appended to the end of the display
    093     * orde. Only used
    094     * when a default model is created automatically.
    095     */
    096    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    097    private String reorder;
    098
    099    /**
    100     * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beaneditor.BeanModel}
    101     * (only used
    102     * when a default model is created automatically).
    103     */
    104    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    105    private String add;
    106
    107    /**
    108     * Specifies the CSS class attribute for the form; the factory default is "well".
    109     */
    110    @Property
    111    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL, value = "message:private-core-components.beaneditform.class")
    112    private String className;
    113
    114    @Component(parameters = "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
    115    private Form form;
    116
    117    /**
    118     * If set to true, then the form will include an additional button after the submit button labeled "Cancel".
    119     * The cancel button will submit the form, bypassing client-side validation. The BeanEditForm will fire a
    120     * {@link EventConstants#CANCELED} event (before the form's {@link EventConstants#VALIDATE} event).
    121     *
    122     * @since 5.2.0
    123     */
    124    @Property
    125    @Parameter
    126    private boolean cancel;
    127
    128    /**
    129     * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
    130     * default bean model will be created from the type of the object bound to the object parameter. The add, include,
    131     * exclude and reorder parameters are <em>only</em> applied to a default model, not an explicitly provided one.
    132     */
    133    @SuppressWarnings("unused")
    134    @Parameter
    135    @Property
    136    private BeanModel model;
    137
    138    @Inject
    139    private ComponentResources resources;
    140
    141    @Inject
    142    private BeanModelSource beanModelSource;
    143
    144    boolean onPrepareFromForm()
    145    {
    146        resources.triggerEvent(EventConstants.PREPARE, null, null);
    147
    148        if (model == null)
    149        {
    150            Class beanType = resources.getBoundType("object");
    151
    152            model = beanModelSource.createEditModel(beanType, resources.getContainerMessages());
    153
    154            BeanModelUtils.modify(model, add, include, exclude, reorder);
    155        }
    156
    157        return true;
    158    }
    159
    160    /**
    161     * Returns the client id of the embedded form.
    162     */
    163    public String getClientId()
    164    {
    165        return form.getClientId();
    166    }
    167
    168    public void clearErrors()
    169    {
    170        form.clearErrors();
    171    }
    172
    173    public boolean getHasErrors()
    174    {
    175        return form.getHasErrors();
    176    }
    177
    178    public boolean isValid()
    179    {
    180        return form.isValid();
    181    }
    182
    183    public void recordError(Field field, String errorMessage)
    184    {
    185        form.recordError(field, errorMessage);
    186    }
    187
    188    public void recordError(String errorMessage)
    189    {
    190        form.recordError(errorMessage);
    191    }
    192}