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