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