0
# Form Processing
1
2
Play Framework's form processing capabilities provide comprehensive support for handling HTML forms, data binding, and form field management. The system supports both dynamic forms for unstructured data and typed forms for model binding with automatic type conversion.
3
4
## Capabilities
5
6
### Dynamic Form Handling
7
8
Dynamic forms handle unstructured form data using a HashMap-backed approach, ideal for forms with variable fields or when you don't have a predefined model.
9
10
```java { .api }
11
/**
12
* Dynamic form backed by HashMap for unstructured data binding
13
*/
14
public class DynamicForm extends Form<DynamicForm.Dynamic> {
15
/** Create empty dynamic form */
16
public DynamicForm();
17
18
/** Create dynamic form with data and errors */
19
public DynamicForm(Map<String,String> data, Map<String,List<ValidationError>> errors, Option<Dynamic> value);
20
21
/** Get field value by key */
22
public String get(String key);
23
24
/** Get all form data as map */
25
public Map<String, String> data();
26
27
/** Fill form with existing data */
28
public DynamicForm fill(Map value);
29
30
/** Bind form from HTTP request */
31
public DynamicForm bindFromRequest(String... allowedFields);
32
33
/** Bind form from data map */
34
public DynamicForm bind(Map<String,String> data, String... allowedFields);
35
36
/** Get field wrapper by key */
37
public Form.Field field(String key);
38
39
/** Get validation error for field */
40
public ValidationError error(String key);
41
42
/** Add validation error */
43
public void reject(String key, String error, List<Object> args);
44
45
/** Convert key to dynamic form key format */
46
static String asDynamicKey(String key);
47
48
/** Convert dynamic form key to normal key format */
49
static String asNormalKey(String key);
50
}
51
52
/**
53
* Data structure for dynamic form data
54
*/
55
public static class DynamicForm.Dynamic {
56
/** Default constructor */
57
public Dynamic();
58
59
/** Constructor with initial data */
60
public Dynamic(Map data);
61
62
/** Get underlying data map */
63
public Map getData();
64
65
/** Set data map */
66
public void setData(Map data);
67
}
68
```
69
70
**Usage Examples:**
71
72
```java
73
import play.data.Form;
74
import play.data.DynamicForm;
75
76
// Create and bind dynamic form
77
public Result handleContactForm() {
78
DynamicForm contactForm = Form.form().bindFromRequest();
79
80
if (contactForm.hasErrors()) {
81
return badRequest(contactForm.errorsAsJson());
82
}
83
84
String name = contactForm.get("name");
85
String email = contactForm.get("email");
86
String message = contactForm.get("message");
87
88
// Process form data
89
return ok("Thank you " + name + "! We'll contact you at " + email);
90
}
91
92
// Pre-fill dynamic form
93
public Result editSettings() {
94
Map<String, String> existingData = new HashMap<>();
95
existingData.put("theme", "dark");
96
existingData.put("language", "en");
97
98
DynamicForm settingsForm = Form.form().fill(existingData);
99
return ok(views.html.settings.render(settingsForm));
100
}
101
```
102
103
### Typed Form Handling
104
105
Typed forms provide strong type safety by binding form data directly to Java model classes with automatic validation and type conversion.
106
107
```java { .api }
108
/**
109
* Helper to manage HTML form description, submission and validation
110
*/
111
public class Form<T> {
112
/** Create dynamic form */
113
public static DynamicForm form();
114
115
/** Create typed form for given class */
116
public static <T> Form<T> form(Class<T> clazz);
117
118
/** Create named typed form */
119
public static <T> Form<T> form(String name, Class<T> clazz);
120
121
/** Create typed form with validation group */
122
public static <T> Form<T> form(String name, Class<T> clazz, Class<?> group);
123
124
/** Bind form from HTTP request */
125
public Form<T> bindFromRequest(String... allowedFields);
126
127
/** Bind form from HTTP request with explicit request object */
128
public Form<T> bindFromRequest(Http.Request request, String... allowedFields);
129
130
/** Bind form from request data map */
131
public Form<T> bindFromRequest(Map<String,String[]> requestData, String... allowedFields);
132
133
/** Bind form from data map */
134
public Form<T> bind(Map<String,String> data, String... allowedFields);
135
136
/** Bind form from JSON data */
137
public Form<T> bind(com.fasterxml.jackson.databind.JsonNode data, String... allowedFields);
138
139
/** Fill form with existing model instance */
140
public Form<T> fill(T value);
141
142
/** Get form data as string map */
143
public Map<String,String> data();
144
145
/** Get typed form value if no errors */
146
public Option<T> value();
147
148
/** Get form value, throws if errors exist */
149
public T get();
150
151
/** Check if form has any validation errors */
152
public boolean hasErrors();
153
154
/** Check if form has global (non-field) errors */
155
public boolean hasGlobalErrors();
156
157
/** Get all global errors */
158
public List<ValidationError> globalErrors();
159
160
/** Get single global error (null if none) */
161
public ValidationError globalError();
162
163
/** Get all validation errors */
164
public Map<String,List<ValidationError>> errors();
165
166
/** Get errors as JSON */
167
public com.fasterxml.jackson.databind.JsonNode errorsAsJson();
168
169
/** Get errors as JSON with specific language */
170
public com.fasterxml.jackson.databind.JsonNode errorsAsJson(play.i18n.Lang lang);
171
172
/** Get field wrapper by key */
173
public Field field(String key);
174
175
/** Get field wrapper by key (alias for field) */
176
public Field apply(String key);
177
178
/** Get form name */
179
public String name();
180
181
/** Discard errors and return clean form */
182
public Form<T> discardErrors();
183
184
/** Add global validation error */
185
public void reject(String error);
186
187
/** Add field-specific validation error */
188
public void reject(String key, String error);
189
}
190
```
191
192
**Usage Examples:**
193
194
```java
195
import play.data.Form;
196
import play.data.validation.Constraints.*;
197
198
// Model class with validation annotations
199
public class User {
200
@Required
201
@MinLength(2)
202
public String name;
203
204
@Required
205
206
public String email;
207
208
@Min(18)
209
public Integer age;
210
211
public String bio;
212
}
213
214
// Controller using typed form
215
public class UserController extends Controller {
216
217
public Result showCreateForm() {
218
Form<User> userForm = Form.form(User.class);
219
return ok(views.html.createUser.render(userForm));
220
}
221
222
public Result createUser() {
223
Form<User> userForm = Form.form(User.class).bindFromRequest();
224
225
if (userForm.hasErrors()) {
226
return badRequest(views.html.createUser.render(userForm));
227
}
228
229
User user = userForm.get();
230
// Save user to database
231
userRepository.save(user);
232
233
return redirect(routes.UserController.showUser(user.id));
234
}
235
236
public Result editUser(Long id) {
237
User existingUser = userRepository.findById(id);
238
Form<User> userForm = Form.form(User.class).fill(existingUser);
239
return ok(views.html.editUser.render(userForm));
240
}
241
}
242
```
243
244
### Form Field Management
245
246
Form fields provide detailed access to field values, validation errors, and metadata.
247
248
```java { .api }
249
/**
250
* Represents a form field with value and validation information
251
*/
252
public static class Form.Field {
253
/** Get field name */
254
public String name();
255
256
/** Get field value */
257
public String value();
258
259
/** Get field value or default if empty */
260
public String valueOr(String or);
261
262
/** Get validation errors for this field */
263
public List<ValidationError> errors();
264
265
/** Get field constraints */
266
public List<Tuple<String,List<Object>>> constraints();
267
268
/** Get field format information */
269
public Tuple<String,List<Object>> format();
270
271
/** Get indexes for repeated fields */
272
public List<Integer> indexes();
273
274
/** Get sub-field by key */
275
public Field sub(String key);
276
}
277
```
278
279
### Form Display Configuration
280
281
Configure form element display names and attributes using annotations.
282
283
```java { .api }
284
/**
285
* Annotation to define display properties for form elements
286
*/
287
@interface Form.Display {
288
/** Display name for the field */
289
String name();
290
291
/** Additional display attributes */
292
String[] attributes() default {};
293
}
294
```
295
296
**Usage Examples:**
297
298
```java
299
public class Product {
300
@Form.Display(name = "Product Name")
301
@Required
302
public String name;
303
304
@Form.Display(name = "Price (USD)", attributes = {"currency", "decimal"})
305
@Min(0)
306
public BigDecimal price;
307
}
308
309
// Accessing field information in templates
310
public Result showProductForm() {
311
Form<Product> productForm = Form.form(Product.class);
312
Form.Field nameField = productForm.field("name");
313
314
String displayName = nameField.name(); // Gets display name from annotation
315
List<ValidationError> errors = nameField.errors();
316
317
return ok(views.html.productForm.render(productForm));
318
}
319
```
320
321
## Advanced Usage Patterns
322
323
### Partial Field Binding
324
325
Control which fields are bound from requests for security and data integrity.
326
327
```java
328
// Only bind specific fields from request
329
Form<User> userForm = Form.form(User.class)
330
.bindFromRequest("name", "email"); // Only these fields will be bound
331
332
// Useful for preventing mass assignment vulnerabilities
333
Form<User> userForm = Form.form(User.class)
334
.bindFromRequest("name", "email", "bio"); // Admin fields excluded
335
```
336
337
### JSON Data Binding
338
339
Bind forms directly from JSON data for API endpoints.
340
341
```java
342
public Result createUserFromJson() {
343
JsonNode json = request().body().asJson();
344
Form<User> userForm = Form.form(User.class).bind(json);
345
346
if (userForm.hasErrors()) {
347
return badRequest(userForm.errorsAsJson());
348
}
349
350
User user = userForm.get();
351
return created(Json.toJson(user));
352
}
353
```
354
355
### Custom Error Handling
356
357
Add custom validation errors programmatically.
358
359
```java
360
public Result validateUser() {
361
Form<User> userForm = Form.form(User.class).bindFromRequest();
362
363
// Custom validation logic
364
if (userService.emailExists(userForm.field("email").value())) {
365
userForm.reject("email", "This email is already registered");
366
}
367
368
if (userForm.hasErrors()) {
369
return badRequest(views.html.createUser.render(userForm));
370
}
371
372
return ok("User is valid");
373
}
374
```