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