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 }