001 // Copyright 2007, 2008, 2009, 2010, 2011 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
015 package org.apache.tapestry5.corelib.components;
016
017 import org.apache.tapestry5.*;
018 import org.apache.tapestry5.annotations.*;
019 import org.apache.tapestry5.beaneditor.BeanModel;
020 import org.apache.tapestry5.internal.beaneditor.BeanModelUtils;
021 import org.apache.tapestry5.ioc.annotations.Inject;
022 import 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)
051 public 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: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 @Component(parameters = "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
108 private Form form;
109
110 /**
111 * If set to true, then the form will include an additional button after the submit button labeled "Cancel".
112 * The cancel button will submit the form, bypassing client-side validation. The BeanEditForm will fire a
113 * {@link EventConstants#CANCELED} event (before the form's {@link EventConstants#VALIDATE} event).
114 *
115 * @since 5.2.0
116 */
117 @Property
118 @Parameter
119 private boolean cancel;
120
121 /**
122 * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
123 * default bean model will be created from the type of the object bound to the object parameter. The add, include,
124 * exclude and reorder parameters are <em>only</em> applied to a default model, not an explicitly provided one.
125 */
126 @SuppressWarnings("unused")
127 @Parameter
128 @Property
129 private BeanModel model;
130
131 @Inject
132 private ComponentResources resources;
133
134 @Inject
135 private BeanModelSource beanModelSource;
136
137 boolean onPrepareFromForm()
138 {
139 resources.triggerEvent(EventConstants.PREPARE, null, null);
140
141 if (model == null)
142 {
143 Class beanType = resources.getBoundType("object");
144
145 model = beanModelSource.createEditModel(beanType, resources.getContainerMessages());
146
147 BeanModelUtils.modify(model, add, include, exclude, reorder);
148 }
149
150 return true;
151 }
152
153 /**
154 * Returns the client id of the embedded form.
155 */
156 public String getClientId()
157 {
158 return form.getClientId();
159 }
160
161 public void clearErrors()
162 {
163 form.clearErrors();
164 }
165
166 public boolean getHasErrors()
167 {
168 return form.getHasErrors();
169 }
170
171 public boolean isValid()
172 {
173 return form.isValid();
174 }
175
176 public void recordError(Field field, String errorMessage)
177 {
178 form.recordError(field, errorMessage);
179 }
180
181 public void recordError(String errorMessage)
182 {
183 form.recordError(errorMessage);
184 }
185 }