Play2 Scala: Forms and Validations

Play2 Scala: Forms and Validations

Last updated:
Play2 Scala:  Forms and Validations
Source

Some reminders on how to define and use forms on Play Framework 2.x (using Scala).

HEADS-UP This article refers to the defining of constraints and validations for form data.

HTML Forms are not discussed here. In other words, here you will find the code you might need in a controller, not in a view.

Standalone form with tuples

import play.api.data._
import play.api.data.Forms._

val form = Form(
    tuple(
        "firstname" -> text,
        "lastname" -> text,
        "age" -> optional(number)))

//using your form in a controller action
def myAction = Action {
    implicit request =>

    //you can bind request params to your form
    val formWData = form.bindFromRequest

    if (formWData.hasErrors) {
        //bad request
    } else {
       //process params
    }
}

HEADS-UP If your form has one single attribute, use single instead of tuple.

Case-class-based Form

Say you want to define a login form for your web app; you could also define a Case class and use it and the model for your form.

The advantage of using case classes instead of simple standalone forms as above (which are based upon tuples) is that you can add methods to this class and also use it for pattern matching.

You can also use fold() to achieve this effect somewhat more elegantly: validating form in an Action

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


case class Identity (username: String, password: String, rememberMe: Boolean, token: Option[String])

//now ithe controller code:
object MyController extends BaseController{

    val loginForm = Form(
        mapping(
          "username" -> text,
          "password" -> text,
          "rememberMe" -> default(boolean, false),
          "token" -> optional(text) )(Identity.apply)(Identity.unapply))

    // a controller action for the login
    def login() = Action { implicit request =>

        //the form data is extracted form the HTTP request
        val formWData = loginForm.bindFromRequest

        if (formWData.hasErrors)
          BadRequest
        else
          Ok("Correct request") //must now check username and password to see if they match!
    }
}

Other builtin validators

  • Default values
  "rememberMe" -> default(boolean,false)
  • Minimum/maximum length for text
  "username" -> text(minLength=3),
  "otherField" -> text(maxLength=30),
  "yetAnotherField" -> text(minLength=10,maxLength=40)

Custom validator

You can also define a custom validator for your fields - it needs to be a function that takes an attribute value and returns a Boolean.

For example, you might want to define a timeStep parameter which should only accept strings that can be seen as time steps, such as "1d" (1 hour), "10m" (for 10 minutes) and so on.

Custom validators can be defined on a per-attribute or a per-form basis (for validations that require more than one attribute to be evaluated)

val form = Form(
    tuple(
        "from" -> date("yyyy-MM-dd"),
        "to" -> date("yyyy-MM-dd"),
        //the error message is optional
        "timeStep" -> text.verifying("Invalid pattern found", { txt =>
            val pattern = """\d+[mhdwm]{1}"""
            txt.matches(pattern)
        })))

In order to validate that the date stored in to is larger than the date in from, you would have to define a validator for the whole form instead:

val form = Form(
    tuple(
        "from" -> date("yyyy-MM-dd"),
        "to" -> date("yyyy-MM-dd"),
        "timeStep" -> text)
    verifying("Ending date cannot be earlier than the starting date", fields => fields match{
        case data => data._1 < data._2 // lexicographic order
    }
))

REFERENCES

Dialogue & Discussion