Playing with Forms

 

Note: In this blog, a reference to a javascript has been made but WordPress does not allow Javascript on sites hosted on WordPress.com. In the HTML code, the places where you see script and /script in the code, enclose it with the HTML angle brackets <> and </>

You’ll see

script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script

replace with

<s ....type="text/javascript"> </s..>

This blog focusses on how to use Forms in Play Framework which is used to create web applications in Java and Scala. The examples in this blog use Scala. I do not intend to cover basics of Play framework as you can find several tutorials on the subject. I assume you are familiar with Play.

A web application would most likely require a user to send some data to the server. The most common use case could be the registration process on a website. When we create an account (Gmail, Facebook etc.) we provide our details by filling a form on the website. This form is generally an HTML form which could look like follows:

facebook_form

Note: To set right expectation, I will not attempt to make Facebook’s homepage.

Once the user fills their data, this data is sent to the server for processing. The Play framework uses Form (notice the distinction I have made by using capital F in Form) to capture or bind this data into a structure which could be easily used for further processing. Let us discuss this further with an example. I will create a small web application in which a user can enter their name. On clicking submit button, this information will be sent to the server and displayed back to the user.

form_reg_basic

After entering a name (say we entered John Cena) and clicking submit, following screen would be displayed

form_basic_regcomplete

 

I’ll start by showing steps for creating a project in Play which can be opened in IntelliJ IDE.

C:\...>play new website_registration
_
_ __ | | __ _ _ _
| '_ \| |/ _' | || |
| __/|_|\____|\__ /
|_| |__/

play 2.2.6 built with Scala 2.10.3 (running Java 1.8.0_111), http://www.playframework.com

The new application will be created in C:\...\website_registration

What is the application name? [website_registration]
>

Which template do you want to use for this new application?

1 - Create a simple Scala application
2 - Create a simple Java application

> 1
OK, application website_registration is created.

Have fun!

C:\...>cd website_registration

C:\...\website_registration>play idea

The command above should create a project which could be opened in Play IDE. Following is the code in various project files

Project Structure

project_structure_basic_form

Application.scala

package controllers

import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import play.api.data.format.Formats._

case class User(name:String)

object Application extends Controller {

  //create a Form. This will map incoming request to an object of type User
  val userForm = Form(
    mapping(
      "name"->of[String]
    )(User.apply)(User.unapply))

  def index = Action {
    Ok(views.html.index("Welcome")(userForm))
  }

  def regSubmit = Action { implicit request  =>
    Ok(views.html.regconf("Registration Successful")(userForm.bindFromRequest))
  }

}

index.scala.html
@(title:String)(form:Form[User])
@main(title)(form)


main.scala.html
@(title: String)(form:Form[User])

<!DOCTYPE html>

<html>
 <head>
 <title>@title</title>
 <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
 <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script
</head> 
<body> 
<form name="registration_form" action="/registration" method="post"> 
<h1>Please enter your details:</h1> 
Name:<input type="text" name ="name"> 
<input type="submit" value="Submit"> 
</form>
 </body> 
</html> 

regconf.scala.html @(title: String)(form:Form[User])
 <!DOCTYPE html> <html>
 <head> 
<title>@title</title> 
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
 <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")"> 
script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script

</head> 
<body>
 <h1> Thank for registering @(form.get.name)</h1>
 </body> 
</html> 

routes 
# Routes 
# This file defines all application routes (Higher priority routes first) 
# ~~~~ # Home page 
GET / controllers.Application.index 
POST /registration controllers.Application.regSubmit 
# Map static resources from the /public folder to the /assets URL path 
GET /assets/*file controllers.Assets.at(path="/public", file)

Back to Forms

Now let us get to the main topic of this blog, understanding Form. The first HTML page has an input text box. The name of this textbox (in HTML code) is “name” (check main.scala.html code). Once data is submitted by the user (clicking submit button), it is received by our application. Instead of processing the incoming message in its raw format, we use a Form (and its methods) to ‘map’ the data into a structure (User case class). This will make processing of incoming request much easier. The term processing could mean validating that the data is correct, manipulate the data, store it etc. For example, we are able to easily refer to the data (name) entered by the user using @(form.get.name) in regconf.scala.html

mapping

The first thing we need to do is define the structure of our form. In above example, our form should ‘map’ to the HTML textbox “name” from the incoming request. ‘name’ in the incoming request is of type text (String). We use the mapping function to define this relationship as follows:

    mapping("name"->of[String])(User.apply)(User.unapply)

The ‘mapping’ method is defined in the Forms object (notice, Form is a class which maps to incoming request but Forms is an object which contains some helper functions). ‘mapping’ is a helper function to create an object which extends the trait Mapping. Mapping trait defines basic functions which bind a data to a Form. It contains methods like bind, constraints etc.). FieldMapping case class is one implementation of Mapping trait.

Following is the definition of mapping in Forms object. There are few other implementations of mapping method which vary in the number of arguments. In the two examples below, the first one has arguments a1, apply and unapply, the second one has a1,a2, apply and unapply. All arguments of type a1, a2 etc. are of type (String, Mapping[])

def mapping[R, A1](a1: (String, Mapping[A1]))(apply: (A1) ⇒ R)(unapply: (R) ⇒ Option[A1]): Mapping[R]

def mapping[R, A1, A2](a1: (String, Mapping[A1]), a2: (String, Mapping[A2]))(apply: (A1, A2) ⇒ R)(unapply: (R) ⇒ Option[(A1, A2)]): Mapping[R]

 

In our example, as we are binding to only one field (“name”), we will use first definition of mapping (with a1, apply and unapply arguments). Here, the mapping function takes 3 arguments. The first argument (a1) takes value “name”->of[String]. It ‘describes’ the incoming request with which this form binds to. ‘a1’ is of type (String, Mapping[String]). The ‘of’ method creates Mapping[String] from String. In general, the field on left of -> is the name of the field in the incoming request (as this is always name of the field, it is always of type String), the field on the right of -> is of type Mapping and can be considered as a constraint i.e. it describes what the field on the left should be (in our case, it should be text or String) because “name” in our HTML form is a textbox.

The reason to call the field on right side of ->as a constraint is because a Mapping also contains constraints. Using these constraints, we can specify not only the type (string, number, email, date) but also additional restrictions like the field cannot be empty or a number cannot be negative or minimum or maximum values of numbers, dates etc. Forms object provides several pre-built constraints which we can use. Some of them are text (maps to String), nonEmptyText (a non-empty String), number (maps to Integer), boolean (maps to Boolean) etc. Using these constraints, we can specify not only the type of data but also permissible values of data. We will discuss more constraints later.

The apply and unapply arguments contain the logic for how to convert the type of field (from incoming request, in our case text or String) into some other type (in our case User.name) and vice versa (i.e. User.name to text or String). Note that though we can map an incoming request into a Form and can use that Form (in our example, userForm) in rest of the code, the ability to convert data from Form into an object of type User gives us more flexibility in processing the data. As User is a case class, we do not need to provide special logic for apply and unapply. We can simply call User’s apply and unapply methods.

Note: Subsequent examples will use the pre-built constraints. Thus the previous code has been changed to following for remaining sections of the blog.

mapping(
  "name"->text
)(User.apply)(User.unapply)

bind

Till this point, we have only written code which provides the structure (or skeleton) of the Form. We also need to write code which takes the actual incoming request and fills the Form with data from the incoming request. We do this by calling bindFromRequest method in Form. Note that bindFromRequest returns a copy of this form filled with the new data. So our Form, userForm itself is not changed.

userForm.bindFromRequest

You’ll notice that we do not pass the incoming request though to bindFromRequest. It is because bindFromRequest expects an implicit request.

def bindFromRequest()(implicit request: Request[_]): Form[T]

Handling Errors

In case the incoming request does not contain all the required fields required by mapping function then mapping would fail. Add a new field ‘age’ in User case class and the mapping method as follows:

case class User(name:String, age:String)

val userForm = Form(
    mapping(
      "name"->text,
      "age"->text
    )(User.apply)(User.unapply))

If you run the above code, you’ll get an exception after clicking the submit button

basicformexception

Though the variable ‘form’ has got the field ‘name’, we got the exception because earlier, bindFromRequest failed (because of missing ‘age’ parameter expected by bindFromRequest method) and the form variable passed to regconf.scala.html was invalid.

To correct this code, we should add a field named ‘age’ in main.scala.html as follows:

Name:<input type="text" name ="name">
Age: <input type="text" name ="age"> 

Though adding above code would solve the problem, it would be better if we could handle the error more elegantly if bindFromRequest fails. We can use hasErrors field in Form to find out if an error has occurred in the binding process. In case there is an error, we can use a different web page to indicate an error.

Change Application.scala as follows:

def regSubmit = Action { implicit request  =>
  val submittedRequest = userForm.bindFromRequest
  if (submittedRequest.hasErrors == false)
  Ok(views.html.regconf("Registration Successful")(submittedRequest))
  else
    Ok(views.html.Error("Registration failed"))
}

Create a new HTML template Error.scala.html in views package

@(title: String) 
<!DOCTYPE html> 
<html> 
<head> 
<title>@title</title> 
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")"> 
<link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script
</head> 
<body> 
<h1> Internal Error</h1> 
</body>
 </html>

Now on clicking submit button, you should get a new web page with ‘Internal Error’ message.

Inbuilt Status and Error messages

We used ‘Ok’ to return a response to the client. Without getting into details of how HTTP (not HTML) works, when a client (web browser) sends a message (Request) to the server, the server responds with a status code (Response). The Request is denoted by GET, POST, DELETE etc (if you look at the routes files, you’ll notice that we use these words to represent incoming request). The Response is generally a number (for example, Ok actually means sending number 200 to the client which denotes that everything was fine with the request). You can find detailed tutorials online on how HTTP works. What is important for our application is that we know how to send a response if we could successfully (or couldn’t) process a request.

Ok means everything is fine. Well actually, in our last example, everything was not fine. We couldn’t bind the request. So we shouldn’t send Ok (200) to the client. Instead, we should send some other response code which denotes an error (at present, our response is that everything is Ok and in the content of the response, we are stating that there was an error!). In Play framework, just like Ok, we can use other methods to represent other status codes. Some examples are BadRequest, InternalServerError etc. To denote that an error occurred, we can change the previous code by replacing Ok with InternalServerError

InternalServerError(views.html.Error("Registration failed"))

If you run the code with above example, you’ll note see any change because the change we have made (Ok to InternalServerError) is in HTTP layer, not HTML layer. HTML represents the content of our page (views.html.Error) which was the same for both cases. However, it is better to send appropriate HTTP response codes as they can give more flexibility and robustness to our application (example if the client application looks at HTTP response to decide client-side logic, for example in web services).

Note: In case you are interested in more details about available error responses, check out Results.scala. The following answer was picked from StackOverflow. It is a reply to a question I asked about implementation of Ok and other similar methods

In Scala (on the example of Play 2.5.x, the idea is similar in other versions as well):

The source of the Ok (BadRequest is very similar): https://github.com/playframework/playframework/blob/2.5.x/framework/src/play/src/main/scala/play/api/mvc/Results.scala#L497 /** Generates a ‘200 OK’ result. */
val Ok = new Status(OK) So, Ok is the object, and Ok("some string") is the object in function notation. In the objects notation it is Ok.apply("some string") The source of the Status code: https://github.com/playframework/playframework/blob/2.5.x/framework/src/play/src/main/scala/play/api/mvc/Results.scala#L362-L379

In case the incoming request has more data fields than mentioned in mapping method, the extra fields will be ignored and will not generate an error. For example, if we add an extra field (address) in main.scala.html code, we will not get an exception like before as the extra field will be ignored

Name:<input type="text" name ="name"> 
Age: <input type="text" name ="age"> 
Address: <input type="text" name ="address"> 

Constraints and Validations

Run the previous example without entering any data for Name, Age and Address fields (keep them empty). On clicking submit you should get a page saying:

Thank for registering

Alternatively, write ’19’ for Name, ‘John’ for Age and ‘*$’ for Address. You’ll get the following message in response!

Thank for registering 19

Do you see the problem? The problem with our implementation is that even though we have sent all the fields in the message (name, age ) with which the server could correctly bind the Form to, the values in these fields is not correct. To ensure that the form contains correct values, we can add constraints to the fields and do form validation at time of submission. This way we can prompt the user to correct the mistakes. One way to put constraints on the fields is to use helper methods available in Forms object

Forms object

First, note that the heading of this section is Forms. Forms is an object. We have already used some helper method from Forms – mapping and text. Forms object contains several helper methods which we can use in the mapping method to put constraints on the fields. Remember it was mentioned earlier that in mapping, the field to right side of -> is a constraint. So far we have used the constraint that name and age should be text. This constraint allows using empty values. Keeping constraint for age as text isn’t correct either as age should be a number. To correct these mistakes, we simply have to use a different type of constraint for name and age. Change the code in mapping as follows which would disallow empty values for name and only numeric values for age.

val userForm = Form(
  mapping(
    "name"->nonEmptyText,
    "age"->number,
  )(User.apply)(User.unapply))

If you now submit the form with empty values of name and age then the Form binding will fail and corresponding error code will be executed (displaying Internal Error page)

Let me explain a bit more about errors in Form binding. It was mentioned earlier that hasErrors is used to find if Form has errors or not. Once we know that a form has errors, we can use ‘errors’ and globalErrors variables to find the specific errors. They are defined as Seq[FormError]’ in Form class. In the following code, Error.scala.html is changed such that we can pass ‘errors’ and globalErrors to it and print them.

Application.scala

def regSubmit = Action { implicit request =>
 val submittedRequest = userForm.bindFromRequest
 if (submittedRequest.hasErrors == false)
 Ok(views.html.regconf("Registration Successful")(submittedRequest))
 else
 InternalServerError(views.html.Error("Registration failed")( submittedRequest.errors)(submittedRequest.globalErrors))

}

Errors.scala.html

@(title: String)(errors:Seq[FormError])(globalErrors:Seq[FormError])

<!DOCTYPE html>

<html>
<head>
 <title>@title</title>
 <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
 <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">

script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script
</head> 
<body> 
<h1> Internal Error</h1> 
@for(x<-errors){
 <p> @x </p> 
@for(x<-globalErrors) {
 <p> @x </p> 
} 
</body>
 </html>

Executing our code with empty values for name and age will generate the following error

Internal Error

FormError(name,error.required,WrappedArray())

FormError(age,error.number,List())

The first two parameters of FormError are of interest. The first one specifies the field name for which the error occurred (name and age), the second one mentions the type of error (name is required and age should be a number).

The Forms object has defined several useful constraints which can be used in mapping for Char, Boolean, Float, List, Seq and other types. As we go along in this blog, we will hopefully encounter some more of them.

Custom Constraints (Constraint and verifying)

In addition to using pre-defined constraints (text, nonEmptyText, number etc.),  we can create our own constraint using Constraint object and ‘verifying’ and ‘validate’ methods.

The verifying method constructs a new Mapping based on on existing mapping, by adding a new ad-hoc constraint. It is defined in mapping trait as follows

def verifying(error: ⇒ String, constraint: (T) ⇒ Boolean): Mapping[T]
def verifying(constraint: (T) ⇒ Boolean): Mapping[T]

The ‘error’ parameter describes the error. In both versions, a function argument is passed (constraint) which takes an input of type T and returns a Boolean value (true/false). true means that the constraint passed, false means that the constraint failed.

To see verifying in action, let us create a constraint for ‘address’ field. To begin with, let us first add ‘address’ to our User class

case class User(
                 name:String,
                 age:Int,
                 address:String)

and change the mapping function

val userForm = Form(
  mapping(
    "name"->nonEmptyText,
    "age"->number,
    "address"->nonEmptyText
  )(User.apply)(User.unapply))

At present, the only constraint we have on the address field is that it should be a non-empty text. Let us say we want to add another constraint that the address must be a P.O Box (in other words, we expect the address to begin with string “P.O.”. We can do so by calling the verifying method on nonEmptyText as follows:

"address"->nonEmptyText.verifying("address error",x=> (x.startsWith("P.O", 0))

It was mentioned earlier that the field on right side of -> is of type Mapping. ‘verifying’ is a method defined in Mapping (thus we can call nonEmptyText.verifying). The ‘address error’ is the message which will be displayed to the user. Run the code with empty address field. We will get two errors related to the address field. The first one (error.required) indicates that ‘address’ is a mandatory field. The second one ‘address error’ comes from our custom constraint. The address is empty, thus, it doesn’t start with string “P.O.”

customerconstraintsexample_addressfield

We can achieve the same result by using a Constraint and passing it to the verifying method Following is the definition of Constraint

Constraint(name: Option[String], args: Seq[Any])(f: (T) ⇒ ValidationResult)

The more important bit is the 2nd curried argument, f, which defines a function that takes a parameter of some type T and returns ValidationResult (Valid or Invalid).

val customConstraint:Constraint[String] = Constraint[String]("custom valildation error")((x=>
  if (x.startsWith("P.O",0) == true) Valid else Invalid(Seq(ValidationError("Address must start with P.O")))))

In defining customerConstraint, we declare it of type Constraint[String] because we will apply the constraint on the address which is a text field. The 1st argument is a name given to this constraint. The second argument is a function literal which would take the String and perform validation on it. the result of this function literal should be of type Valid or Invalid. The Invalid message is the message which will be generated if the constraint fails (for the user or application to know what went wrong).

Let us now add the constraint to address field

val customConstraint:Constraint[String] = Constraint[String]("custom valildation error")((x=>
  if (x.startsWith("P.O",0) == true) Valid else Invalid(Seq(ValidationError("Address must start with P.O")))))

val userForm = Form(
  mapping(
    "name"->nonEmptyText,
    "age"->number,
    "address"->nonEmptyText.verifying(customConstraint)
    )(User.apply)(User.unapply)) //if we use verifying here, the Constraint has to be of type Constraint[User]

Executing the program with empty address field should generate similar result as the previous example but the string “address error” will be replaced by “Address must start with P.O”

Putting constraint on entire form

Similar to putting constraints on individual fields of a Form, we can put constraints at a single place for the entire form. See following code in which a Constraint is created which will be applied to the Form. Note that just like when we applied the Constraint to a String, we used Constraint[String], in same way, this Constraint will be applied to User and is thus to type Constraint[User]

val customFormConstraint:Constraint[User] = Constraint[User]("form constraint")(form => {
  if (form.name.isEmpty) Invalid(ValidationError("Name cannot be empty"))
  else if (form.age < 0 ) Invalid(ValidationError("Age cannot be negative"))
  else if (form.address.isEmpty) Invalid(ValidationError("Address cannot be empty"))
  else if(form.address.startsWith("P.O",0) != true) Invalid(Seq(ValidationError("Address must start with P.O")))
  else
  Valid
})

val userForm = Form(
  mapping(
    "name"->text,
    "age"->number,
    "address"->text
  )(User.apply)(User.unapply).verifying(customFormConstraint)
)

As mapping method returns object of type Mapping and verifying method is defined in Mapping, we can use verifying method on the Mapping returned by mapping

Note: While running this code, do not keep the age empty because age is of type number. If it is kept empty, the binding will fail because of error.number exception.

views.html.helper

So far, we have written the HTML code. Instead, we can use the helper package which contains several functions which can create template code for HTML for us. So instead of writing following code to create an input text field

<input type="text" name ="name">

we can use following helper code

@helper.inputText(form("name"))

The above code will create an input field of type =”text”. The name of the input field’s would be same as the name of the Form’s field (“name”).

We can use the helper functions to create the HTML code as follows:

@(title: String)(form:Form[User])

<!DOCTYPE html>

<html>
 <head>
 <title>@title</title>
 <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
 <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script

</head> 
<body> 
<h1>Please enter your details:</h1> 
@helper.form(action=routes.Application.regSubmit){ 
@helper.inputText(form("name")) 
@helper.inputText(form("age")) 
address:<input type = "text" value="address"/> 
<input type="submit" value="submit"/> 
} 
</body>
 </html>

If you run the above code, the first thing which immediately comes out is that in addition to displaying the input fields, the page also tells the user about the type and/or constraints application for that field. This happens because of FieldConstructor. We will discuss them later.

helper_example

In addition to specifying the constraints for a field, HTML input controls created using helper package can also handle Form errors automatically. Make following change in Application.scala and notice how the helper function notifies the user about issues with form submission (this functionality also comes from FieldConstructor)

Application.scala

def regSubmit = Action { implicit request  =>
  val submittedRequest = userForm.bindFromRequest
  if (submittedRequest.hasErrors == false)
    Ok(views.html.regconf("Registration Successful")(submittedRequest))
  else
   BadRequest(views.html.index("Registration failed")( submittedRequest))
}

helper_error_handling_example

If there is a problem in binding a form (assuming that the issue was with data in the form and not any other internal processing error like a missing field), the index page is displayed again. If you recollect, the index page is the first page for our web application. We pass two parameters to it, title and a Form. The index page calls ‘main’ to display fields of the form. When the index page is called for the first time, the form passed to it is empty. But this time, we pass the Form which gets created after processing the incoming request. As bindFromRequest failed, it creates a copy of the form and fills the ‘errors’ variable (discussed earlier). So the Form we now passed to index contains information about errors. The helper functions (inputText for example) used this error information and displayed it as well.

There are several methods available in helper package which could be used to generate HTML text like inputDate, inputPassword, select etc.

If you know HTML programming, you would know that the HTML code consists of elements and attributes. In example below, ‘input’ is an element and ‘type’ and ‘name’ are attributes with values ‘text’ and ‘name’. In addition to these two attributes, we can specify several other attributes, for example ‘placeholder’ which provides a helpful description about  an input field.  If we use helper package, we can specify the various additional arguments which we might use if we were writing HTML directly. For example, we can create a text field in HTML as follows:

Name: <input type="text" name="name" placeholder="Enter name">

html_attribute_example_without_helper

We can mention these attributes in methods in helper package as well by using ‘attributename->value format. For example, the  above example could be written using helper methods as follows

@helper.inputText(form("name"), '_help->"Enter name", 'placeholder->"Enter name")

html_attribute_example_with_helper

 

Following is the signature of the ‘apply’ method of inputText

def apply(field: Field, args: (Symbol, Any)*)(implicit handler: FieldConstructor, lang: Lang): play.api.templates.HtmlFormat.Appendable

All extra parameters to a helper method will be added to the generated Html through args argument of the apply function of that helper method, unless they start with the _ character. Arguments starting with _ are reserved for field constructor arguments (covered next). So the above helper method translates to following:

helper.inputText(field = form("name"), args = 'placeholder -> "Enter name")

Field and FieldConstructor

A Form consists of fields represented by Field case class. These fields get created when we create the Form object. Following statement creates 3 Fields with names “name”, “age” and “address” in our Form object, userForm.

val userForm = Form(
  mapping(
    "name"->text,
    "age"->number,
    "address"->text
  )(User.apply)(User.unapply).verifying(customFormConstraint)
)

We can access these fields of the form by passing the name of the field as an argument to the form object. We already used this in our previous example where form is the object passed as an argument to the HTML file.

@(title: String)(form:Form[User])
.
.
.
@helper.inputText(form("name"))

The statement above will return a Field with name “name”. This happens because the apply function of Form takes a string and returns a Field

def apply(key: String): Field

The Field case class is defined as follows:

case class Field (form: play.api.data.Form[_], name: String, constraints: Seq[(String, Seq[Any])], format: Option[(String, Seq[Any])], errors: Seq[FormError], value:Option[String]) extends Product with Serializable

While creating the form, we had associated constraints by using “name”->nonEmptyText format for example. The name (“name”), constraint (“nonEmptyText”) constraints gets passed to the field as an argument along with other arguments like format, value, errors. In addition to name and constraints, we can also specify format (for example, a field for email should contain @ sign), a value (say a checkbox is checked by default) and errors (say while prompting a user to correct a form, we specify the errors in the previous submission). All these values can be associated with a Field using the Form object. For example, Form has a formats value which can be used to specify format for a field of the form.

val formats: Map[String, (String, Seq[Any])]

FieldConstructor

If you look at the apply function of inputText, you’ll notice that it needs an implicit FieldConstructor. Play framework provides a default Field Constructor available implicitly. A FieldConstructor generates the HTML code which is responsible for laying out a Field. In all our examples, you would have noticed that the fields (name, age, address) are displayed in a specific pattern. For the code below, notice the similarity in the pattern in which the different fields are laid in the browser window.

@(title: String)(form:Form[User])


<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript" /script


</head> 
<body> 
<h1>Please enter your details:</h1>
 @helper.form(action=routes.Application.regSubmit){ 
@helper.inputText(form("name"), 'placeholder->"Enter name") 
@*Name: <input type ="text" name="name" placeholder="Enter name"/>*@ 
@helper.inputText(form("age"))
 @helper.inputText(form("address.street"), '_label->"Street")
 @helper.select(field=form("address.country"), options=List(("united kingdom","UK"),("outside united kingdom","Non-UK"))) 
<input type="submit" value="submit"/> } 
</body> 
</html>

defaultfieldconstructor The entries marked RED are all aligned to left. They each are followed by a GREEN and a BROWN entry aligned slightly towards right one below the other. ‘_help is used as a replacement of placeholder attribute if we use helper methods. This similarity in the pattern is due to the default FieldConstructor used to display these fields. For each input field, the default FieldConstructor creates the HTML code as follows:

//HTML generated for inputText(form("name"))
<dl class=" " id="name_field">
 <dt><label for="name">name</label></dt>
 <dd>
 <input type="text" id="name" name="name" value="" placeholder="Enter name">
</dd>

 <dd class="info">Required</dd>

</dl>

//HTML generated for inputText(form("age"))

<dl class=" " id="age_field">
 <dt><label for="age">age</label></dt>
 <dd>
 <input type="text" id="age" name="age" value="" >
</dd>

 <dd class="info">Numeric</dd>

</dl>

If you are familiar with HTML programming, you would know that a <dl>, <dt> and <dd> tags (known as description list (dl) , with terms (dt) and descriptions (dd)) are used together and the contents within these tags is displayed in this pattern (for each dl, dt is on left and all corresponding dd are on right). You should easily find examples of dl, dt and dd on the internet. As the default FieldConstructor uses the name of the field (“name”) for value of label, we see that name and age are on left (because label is used for dt). The default FieldConstructor then creates the actual element <input…> in a dd (thus the input text box is on right because dd are on right side). It the creates another dd with constraints on that field (eg Numeric, Required) which are also on right. The default field constructor provides various options which we can specify to modify the values which are displayed. We already used ‘_label to specify an alternate label for street. That is why the label for street is ‘Street’ and not ‘address.street’. It was mentioned earlier that arguments starting with _ are reserved for field constructor arguments. The default FieldConstructor provides following arguments

'_label -> "Custom label"
'_id -> "idForTheTopDlElement"
'_help -> "Custom help"
'_showConstraints -> true or false
'_error -> "Force an error"
'_showErrors -> true or false

Let us see their usage by making following changes in two files

main.scala.html

@helper.inputText(form("name"), 'placeholder->"Enter name", '_label -> "What is your Name", '_help -> "First name, Last Name",
    '_showConstraints -> true, '_error -> "Some Error", '_showErrors -> true)

Application.scala

def regSubmit = Action { implicit request =>
  userForm.bindFromRequest.fold({
    formWithErrors => BadRequest(views.html.index("Registration failed")(formWithErrors))
  }, {
    userData => Ok(views.html.regconf("Registration Successful")(userForm.fill(userData)))
  })

What we have done is enabled the _showConstraint and _showErrors parameters of the default FieldConstructor. This way, the FieldConstructor will create an additional <dd> element if the Field has errors. Application.scala has also been changed so that if bind fails, the Form with errors is sent back to the client. To see the changes, start the session and click submit without entering any data.

First screen will not have errors displayed because the initial Form hasn’t got any errors

fieldconstructorexampleinitialform

The HTML generated for name field is (right click->view source code)

<dl class=" " id="name_field">
 <dt><label for="name">What is your Name</label></dt>
 <dd>
 <input type="text" id="name" name="name" value="" placeholder="Enter name">
</dd>
  
 <dd class="info">First name, Last Name</dd>
 
</dl>

Click submit without entering any data

fieldconstructorexampleerrorform

Now the error fields are generated because we have enabled them in ‘_showErrors -> true (this is the default behavior)

The HTML generated for name field is (notice the additional dd element generated as the form has errors)

<dl class=" error" id="name_field">
 <dt><label for="name">What is your Name</label></dt>
 <dd>
 <input type="text" id="name" name="name" value="" placeholder="Enter name">
</dd>
 
 <dd class="error">This field is required</dd>
 
 
 <dd class="info">First name, Last Name</dd>
 
</dl>

In general, the default field constructor lays out the elements using following template

<dl class="error"> //this is error or empty depending on whether the Field has errors or not
  <dt><label for="name"> some text here, either from '_label or name of the Field </label></dt>
  <dd><input type="text" id="name" name="name"></dd> //if using inputText, input will be set to "text"
  <dd class="error">This field is required</dd> //if there are errors 
  <dd class="info">Required</dd> // from constraints
</dl>

Creating custom FieldConstructor

Instead of using default FieldConstructor, we can create our own FieldConstructor which gives to us the flexibility to create our own styling. Following is a code of a custom FieldConstructor picked from PlayFramework documentation.

  • We specify the template. Just like the default FieldConstructor uses a template (shown previously), we need to specify the template we want to use. Create a new file myFieldConstructor.scala.html in views

@(elements: helper.FieldElements)
<div class="@if(elements.hasErrors) {error}">
<label for="@elements.id">@elements.label</label>
<div class="input">
@elements.input
<span class="errors">@elements.errors.mkString(", ")</span>
<span class="help">@elements.infos.mkString(", ")</span></div>
</div>

In above template, we define how we want the different things to appear. helper.FieldElements give us access to the element for which this field constructor is called. If this element has errors, we create class=”error” else we keep it empty (class=””). Then we add a label (for tells HTML the id of the element to which the label applies, the actual label value comes from elements.label), then we create (draw) the actual UI of the element (class=”input”) and then we lay out any error or help information. The div tag adds a line break, the span tag keeps elements in same line. As our template starts with div, each Field will start with new line but error and help information of the Field will stay in the same line as the UI element of the field (you’ll see it in screenshot below). Everytime we use method of helper class (say inputText or select), this template will be called passing the Field (which that method draws). The Field will be drawn as per the template.

  • Once we have the template, we need to create our own Field Constructor which will be used by helper method instead of the default one. Make following changes in Application.scala
import views.html.myFieldConstructor
.
.
.
object MyHelpers {
  import views.html.helper.FieldConstructor
//create new field constructor
  implicit val myFields = FieldConstructor(myFieldConstructor.f)
}
  • So we have the template and a Field Constructor. All we have to do now is make it visible to our helper methods so that they use it. Import the FieldConstructor in main.scala.html
@(title: String)(form:Form[User]) //this line is always the first line
@import controllers.Application._
@import MyHelpers._

The new layout should be visible now. Notice that as we used <span> after input tag, help is in the same line as the input UI field

newfieldconstructor

If you press submit without entering data, error will also show in same line because of use of <span>

newfieldconstructorerrordisplay

RightClick->view source to see the HTML generated by our Field Constructor using our template.

The apply method of helper methods take FieldConstructor as an implicit argument. Thus it is also possible to pass a FieldConstructor object to these methods as follows (@* and *@ are used to comment a code in view’s html files):

@*@import MyHelpers._*@

@helper.inputText(form("name"), 'placeholder->"Enter name", '_label -> "What is your Name", '_help -> "First name, Last Name",
 '_showConstraints -> true, '_error -> "Some Error", '_showErrors -> true)(handler=MyHelpers.myFields, implicitly[Lang])

The option to pass a field constructor gives the flexibility to format different input fields in different ways. In above code, the inputText(form(“name2)) is formatted using HTML template of myFields (single line because of span). The remaining input use default FieldConstructor (multiple lines using dl, dt, dd)

multiplefieldconstructor

It was mentioned earlier that we can pass arguments to inputText, inputDate and other helper methods. These arguments could specific to the HTML element we are using. They could also be specific to the FieldConstructor. In the following code, placeholder is argument to the textbox and arguments starting with underscore (_) are arguments to the default Field Constructor (example _label and _help )

@helper.inputText(form("name"), 'placeholder->"Enter name", '_label -> "What is your Name", '_help -> "First name, Last Name",
    '_showConstraints -> true, '_error -> "Some Error", '_showErrors -> true)

When we create our own field constructor, we can create arguments specific to our field constructor which we can later use in the template. Such arguments must start with an underscore. For example, we can create an additional argument like  _color and use it in our custom field constructor.  These arguments (or “options”) will be available in a Map in elements.args.

main.scala.html

@helper.inputText(form("name"), 'placeholder->"Enter name", '_label -> "What is your Name", '_help -> "First name, Last Name",
    '_showConstraints -> true, '_error -> "Some Error", '_showErrors -> true, 
'_mycolor->"#0000FF")(handler=MyHelpers.myFields, implicitly[Lang])

myFieldConstructor.scala.html

@(elements: helper.FieldElements)

style=”color:@elements.args(‘_mycolor)”> @elements.label

@elements.input @elements.errors.mkString(“, “) @elements.infos.mkString(“, “)

</div>

In above example, we pass _mycolor argument to our field constructor. The field constructor template accesses the argument using args member of FieldElements class. The args is of type Map[Symbol, Any] with Symbol beingkKey and Any being value corresponding to the key. We get the value by passing the key (‘_color) to the Map (args). We have used ‘_color to give color (#0000FF is Blue color) to our div element. The page now will look like follows:

passingargumentstocustomfieldconstructor

 

Some extra bits

  • single and tuple method

An alternative to using mapping method is single and tuple method. They look very similar to mapping. ‘mapping’ maps the data of a Form into an object of some class or case class. A tuple would instead map the data into a tuple. A single would map the data into a single variable.

def single[A1](a1: (String, Mapping[A1])): Mapping[A1]

val userForm = Form (
 single(
  "name"->text 
  ) //notice there is no appply/unapply method. 
)
val nameFromForm= userForm.bindFromRequest.get

def tuple[A1, A2](a1: (String, Mapping[A1]), a2: (String, Mapping[A2])): Mapping[(A1, A2)]

val userForm = Form (
 tuple(
  "name"->text,  
  "age"->number
  ) //notice there is no appply/unapply method. 
)
val (nameFromForm,ageFromForm) = userForm.bindFromRequest.get

 Alternative way to bind with request

Earlier we discussed bindFromRequest method to bind a Form to incoming request. There are two other ways to do the same.

  • fold method

Another way to handle errors is to use fold method of Form. The fold method is defined as follows:

def fold[R](hasErrors: (Form[T]) ⇒ R, success: (T) ⇒ R): R

After binding with the incoming request (or data), either the form has errors or the submission was a success and a concrete value is available. Accordingly, the fold method takes two function literals as arguments to handle these two situations. The first is called if the binding fails, and the second is called if the binding succeeds. Note that the first argument gives us the form with errors field filled in. The second argument gives the value in the form (object of the case class it maps to). Thus in example below, userForm is filled with data (userData) as regconf expects argument of type Form, not User.

def regSubmit = Action { implicit request  =>
  userForm.bindFromRequest.fold({
    formWithErrors=>BadRequest(views.html.Error("Registration failed")( formWithErrors.errors))
  },
  {
    userData=>Ok(views.html.regconf("Registration Successful")(userForm.fill(userData)))
  })

Note the use of fill method to add data into a form. ‘userForm’ is defined as a val, so it is immutable. It holds the mapping. When you use bindFromRequest.fold you are not changing userForm, you are using the mapping information in userForm to generate a new instance of your case class, say userData (or a version of the form with errors in it). Each time you execute that method, you will get a new instance of userData.

  • Nested mapping

So far in our example, the User case class was quite simple and didn’t use any nested classes, lists etc. The real life use cases will not be so straightforward. The following example complicated things a bit by nesting and sequence to the data in HTML form. The example is meant to include different constraints and helper function

case class Address (street: String, country:String)

case class User(
                 name:String,
                 age:Int,
                 address:Address //mapping will be nested
                 )

//nested mapping
val userForm:Form[User] = Form(
  mapping(
    "name" -> nonEmptyText,
    "age" -> number,
    "address" -> mapping(
      "street"->nonEmptyText,
      "country"->nonEmptyText
    )(Address.apply)(Address.unapply)
  )(User.apply)(User.unapply)
)
  • repeat

At times, you might a need to repeat an input. For example, a User could have multiple email ids. Instead of creating two separate inputText fields, we can code inputText to ‘repeat’ itself two times. This is done using repeat object.

First, we will have to change User class to accommodate multiple email ids.

case class User(
                 name:String,
                 age:Int,
                 address:Address,
                 email:List[String])

We will have to change mapping as well.

val userForm:Form[User] = Form(
  mapping(
    "name" -> nonEmptyText,
    "age" -> number,
    "address" -> mapping(
      "street"->nonEmptyText,
      "country"->nonEmptyText
    )(Address.apply)(Address.unapply),
    "email"->list(text)
  )(User.apply)(User.unapply)
)

We then add two input fields in main.scala.html

@helper.repeat(form("email"),2){emailField=>

@helper.inputText(emailField, '_label->"Email"}

Following is apply method of repeat object

def apply(field: Field, min: Int = 1)(fieldRenderer: (Field) ⇒ Html): IndexedSeq[Html]

In above, field is the Field of the form which needs to be repeated (email). Next argument specifies the minimum number of times the repeated field need to appear on web page (we used value 2 in our code). The argument takes the logic of how to render the field. This logic is called for each number of time the field is rendered/displayed on the page. In our case, the code inside {} will be executed two times, once for each email field. The emailField will contain reference of the Field for which the code is called. We use emailField to create te UI of the field in usual way by calling inputText and passing the field to it.

repeat_helper_example

 

 

 

2 thoughts on “Playing with Forms

Leave a comment