The data-mapping library is similar to the Play Framework's Forms.
To use data-mapping, import the following package:
import com.greenfossil.data.mapping.Mapping.*
import com.greenfossil.data.mapping.Mapping
Last modified on 05/12/2022, 3:00 pm Single field mappings can be declared like below:
val textMapping = Mapping("text", text)
val nonEmptyTextMapping = Mapping("text", nonEmptyText)
val boolMapping = Mapping("bool", boolean)
val intMapping = Mapping("int", number)
val localDateMapping = Mapping("date", localDateUsing("yyyy-MM-dd"))
Mappings can also contain multiple fields:
val tupleMapping = tuple(
"text" -> text,
"date" -> localDateUsing("yyyy-MM-dd"),
"int" -> number,
"bool" -> boolean
)
Mappings can be mapped to a case class:case class User(firstname: String, lastname: String)
val userForm = mapping[User](
"firstname" -> text,
"lastname" -> text
)
Mappings can also be defined to have repeated valuesval namesForm: Mapping[Seq[String]] = Mapping("name", seq(text))
val tuplesForm: Mapping[Seq[(String, String)]] = repeatedTuple(
"firstname" -> text,
"lastname" -> text
)
case class User(firstname: String, lastname: String)
val usersForm: Mapping[Seq[User]] = repeatedMapping[User](
"firstname" -> text,
"lastname" -> text
)
A field can sometime be optional:
val optionalTextMapping: Mapping[Option[String]] = Mapping("optional", optional(text))
A field can have a default value. Default values are used when there is no corresponding data for the field from the request.val numberMapping = Mapping("number", default(1))
val dateMapping = Mapping("date", default(localDateUsing("yyyy-MM-dd"), LocalDate.now))
A field can also be made to have a static value using ignoredcase class User(id: Long, firstname: String, lastname: String)
val usersForm: Mapping[Seq[User]] = repeatedMapping[User](
"id" -> ignored(1),
"firstname" -> text,
"lastname" -> text
)
Individual fields can have constraints. By default, nonEmptyText field requires a non empty strings while text field accepts empty strings. Other fields, such as number or localDate requires the submitted data to be of a certain format.
Text fields can be defined to require a certain length:
val textMapping = Mapping("text", text(minLength = 10, maxLength = 30, trim = true))
Similarly, number fields can have min or max value:
val numberMapping = Mapping("number", number(min = 10, max = 30))]
A field can also have ad-hoc constraints:val dobMapping = Mapping(
"dob",
localDate.verifying("Date should not be in the future", !_.isAfter(LocalDate.now))
)
Multiple fields can be verified as a whole:val tupleMapping = tuple(
"min" -> number,
"max" -> number
).verifying("Min has to be smaller or equal to max", (min, max) => min <= max)
Fields can be transformed from one type to another.
In the example below, a comma separated text is transformed to a sequence of String.
val form: Mapping[Seq[String]] = Mapping(
"values", text.transform[Seq[String]](_.split(","), _.mkString(","))
)
You can fill the form using the fill method:
val form = Mapping("name", nonEmptyText)
form.fill("John Doe")
Data can be retrieved from the request using the bindFromRequest() method.
@Post("/submit")
def submitForm = Action{implicit request =>
form.bindFromRequest().fold(
errorForm => ???, // form errors can be obtained from errorForm.errors
data => ??? // successfully bound data
)
}
The form can also be bound manually using key-value pairs:form.bind("firstname" -> "john", "lastname" -> "doe")
A bound form can have individual field errors.
To access the individual fields, we can use the apply methods:
val form = tuple(
"name" -> nonEmptyText.transform[String](_.capitalize, _.capitalize),
"age" -> number
)
val boundForm = form.bind("name" -> "john")
val nameValue = boundForm[String]("name").value // Some("John")
val nameBindingValue = boundForm[String]("name").bindingValue // Some("john")
val ageError = boundForm[Int]("age").errors.map(_.message) // List("error.required")