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.beaneditor.BeanModel; 018import org.apache.tapestry5.internal.beaneditor.BeanModelUtils; 019import org.apache.tapestry5.ioc.annotations.Inject; 020import org.apache.tapestry5.services.BeanModelSource; 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.beaneditor.BeanModel 039 * @see org.apache.tapestry5.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.beaneditor.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.beaneditor.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.beaneditor.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}