Form handling and submission is an important part of any web application. Play comes with features that make handling simple forms easy and complex forms possible.

Play’s form handling approach is based around the concept of binding data. When data comes in from a POST request, Play will look for formatted values and bind them to a Form object. From there, Play can use the bound form to value a case class with data, call custom validations, and so on.

Typically forms are used directly from a Controller instance. However, Form definitions do not have to match up exactly with case classes or models: they are purely for handling input and it is reasonable to use a distinct Form for a distinct POST.

The Forms object defines the mapping method. This method takes the names and constraints of the form, and also takes two functions: an apply function and an unapply function. Because UserData is a case class, we can plug its apply and unapply methods directly into the mapping method.

Note: Maximum number of fields for a single tuple or mapping is 22 due to the way form handling is implemented. If you have more than 22 fields in your form, you should break down your forms using lists or nested values.

A form will create UserData instance with the bound values when given a Map:

But most of the time you’ll use forms from within an Action, with data provided from the request. Form contains bindFromRequest, which will take a request as an implicit parameter. If you define an implicit request, then bindFromRequest will find it.

val userData = userForm.bindFromRequest.get

Note: There is a catch to using get here. If the form cannot bind to the data, then get will throw an exception. We’ll show a safer way of dealing with input in the next few sections.

You are not limited to using case classes in your form mapping. As long as the apply and unapply methods are properly mapped, you can pass in anything you like, such as tuples using the Forms.tuple mapping or model case classes. However, there are several advantages to defining a case class specifically for a form:

Form specific case classes are convenient. Case classes are designed to be simple containers of data, and provide out of the box features that are a natural match with Form functionality.

Form specific case classes are powerful. Tuples are convenient to use, but do not allow for custom apply or unapply methods, and can only reference contained data by arity (_1, _2, etc.)

Form specific case classes are targeted specifically to the Form. Reusing model case classes can be convenient, but often models will contain additional domain logic and even persistence details that can lead to tight coupling. In addition, if there is not a direct 1:1 mapping between the form and the model, then sensitive fields must be explicitly ignored to prevent a parameter tampering attack.

The text constraint considers empty strings to be valid. This means that name could be empty here without an error, which is not what we want. A way to ensure that name has the appropriate value is to use the nonEmptyText constraint.

In the failure case, we render the page with BadRequest, and pass in the form with errors as a parameter to the page. If we use the view helpers (discussed below), then any errors that are bound to a field will be rendered in the page next to the field.

In the success case, we’re sending a Redirect with a route to routes.Application.home here instead of rendering a view template. This pattern is called Redirect after POST, and is an excellent way to prevent duplicate form submissions.

Note: “Redirect after POST” is required when using flashing or other methods with flash scope, as new cookies will only be available after the redirected HTTP request.

Alternatively, you can use the parse.formbody parser that binds the content of the request to your form.

In the failure case, the default behaviour is to return an empty BadRequest response. You can override this behaviour with your own logic. For instance, the following code is completely equivalent to the preceding one using bindFromRequest and fold.

Once you have a form, then you need to make it available to the template engine. You do this by including the form as a parameter to the view template. For user.scala.html, the header at the top of the page will look like this:

@(userForm: Form[UserData])(implicit messages: Messages)

Because user.scala.html needs a form passed in, you should pass the empty userForm initially when rendering user.scala.html:

You can find several input helpers in the views.html.helper package. You feed them with a form field, and they display the corresponding HTML input, setting the value, constraints and displaying errors when a form binding fails.

Note: You can use @import helper._ in the template to avoid prefixing helpers with @helper.

Note: The source code for each of these templates is defined as Twirl templates under views/helper package, and so the packaged version corresponds to the generated Scala source code. For reference, it can be useful to see the views/helper package on Github.

As with the form helper, you can specify an extra set of parameters that will be added to the generated Html:

This is useful because to use CSRF with forms, both a Request (technically a RequestHeader) and a Messages object must be available to the template. By using a MessagesRequest, which is a WrappedRequest that extends MessagesProvider, only a single implicit parameter needs to be made available to templates.

Because you typically don’t need the body of the request, you can pass MessagesRequestHeader, rather than typing MessagesRequest[_]:

When you are using repeated data like this, there are two alternatives for sending the form values in the HTTP request. First, you can suffix the parameter with an empty bracket pair, as in “emails[]”. This parameter can then be repeated in the standard way, as in http://foo.com/request?emails[][email protected]&emails[][email protected]. Alternatively, the client can explicitly name the parameters uniquely with array subscripts, as in emails[0], emails[1], emails[2], and so on. This approach also allows you to maintain the order of a sequence of inputs.

If you are using Play to generate your form HTML, you can generate as many inputs for the emails field as the form contains, using the repeat helper:

Note that Contact contains a Seq with ContactInformation elements and a List of String. In this case, we can combine the nested mapping with repeated mappings (defined with Forms.seq and Forms.list, respectively).