Skip to content

Commit

Permalink
SCALA-229
Browse files Browse the repository at this point in the history
Play! Forms
  • Loading branch information
maenolis committed Nov 23, 2024
1 parent 9839b0d commit 1f1b982
Show file tree
Hide file tree
Showing 10 changed files with 407 additions and 1 deletion.
94 changes: 94 additions & 0 deletions play-scala/play-templates/app/controllers/FormController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package controllers

import com.google.inject.Inject
import models.{
ComplexFormCustomField,
InputFormWithConstraints,
MultipleFieldsForm,
SimpleForm
}
import play.api.i18n.I18nSupport
import play.api.libs.json.Json
import play.api.mvc.{
AbstractController,
Action,
AnyContent,
ControllerComponents
}

class FormController @Inject() (cc: ControllerComponents)
extends AbstractController(cc)
with I18nSupport {

def simpleForm: Action[AnyContent] = Action { implicit request =>
Ok(views.html.Baeldung.FormTemplate(SimpleForm.form))
}

def formPost(): Action[AnyContent] = Action { implicit request =>
val form = SimpleForm.form.bindFromRequest().get
Ok(form.toString)
}

def multipleFieldsForm: Action[AnyContent] = Action { implicit request =>
Ok(views.html.Baeldung.MultipleFieldsFormTemplate(MultipleFieldsForm.form))
}

def multipleFieldsFormPost(): Action[AnyContent] = Action {
implicit request =>
val form = MultipleFieldsForm.form.bindFromRequest().get
Ok(form.toString)
}

def formWithConstraints: Action[AnyContent] = Action { implicit request =>
Ok(
views.html.Baeldung.FormTemplateWithConstraints(
InputFormWithConstraints.form
)
)
}

def formWithConstraintsPost(): Action[AnyContent] = Action {
implicit request =>
InputFormWithConstraints.form
.bindFromRequest()
.fold(
{ formWithError =>
BadRequest(
views.html.Baeldung.FormTemplateWithConstraints(formWithError)
)
},
{ data => Ok(Json.toJson(data)) }
)
}

def simpleFormPostWithErrors(): Action[AnyContent] = Action {
implicit request =>
SimpleForm.form
.bindFromRequest()
.fold(
{ formWithError =>
BadRequest(
views.html.Baeldung.FormTemplateWithErrors(formWithError)
)
},
{ data => Ok(Json.toJson(data)) }
)
}

def complexForm: Action[AnyContent] = Action { implicit request =>
Ok(views.html.Baeldung.ComplexFormTemplate(ComplexFormCustomField.form))
}

def complexFormPostWithErrors(): Action[AnyContent] = Action {
implicit request =>
ComplexFormCustomField.form
.bindFromRequest()
.fold(
{ formWithError =>
BadRequest(views.html.Baeldung.ComplexFormTemplate(formWithError))
},
{ data => Ok(Json.toJson(data)) }
)
}

}
189 changes: 189 additions & 0 deletions play-scala/play-templates/app/models/InputForms.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package models

import models.Measure.UnitMeasurement
import play.api.data.Form
import play.api.data.Forms._
import play.api.data.validation.Constraints.minLength
import play.api.libs.json.{Json, Writes}

import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.{Date, UUID}

case class SimpleForm(i: Int, active: Boolean, msg: String)

object SimpleForm {

implicit val simpleFormWrites: Writes[SimpleForm] = Json.writes[SimpleForm]
val form: Form[SimpleForm] = Form(
mapping(
"i" -> number,
"active" -> boolean,
"msg" -> text
)(SimpleForm.apply)(SimpleForm.unapply)
)

def unapply(simpleForm: SimpleForm): Option[(Int, Boolean, String)] = {
Some((simpleForm.i, simpleForm.active, simpleForm.msg))
}

}

case class MultipleFieldsForm(
i: Int,
pwd: String,
active: Boolean,
msg: String,
date: Date,
uuid: UUID,
favMovie: String,
favDrink: String
)

object MultipleFieldsForm {

implicit val multipleFieldsFormWrites: Writes[MultipleFieldsForm] =
Json.writes[MultipleFieldsForm]
val form: Form[MultipleFieldsForm] = Form(
mapping(
"i" -> number,
"pwd" -> text,
"active" -> boolean,
"msg" -> text,
"date" -> date,
"uuid" -> uuid,
"favMovie" -> text,
"favDrink" -> text
)(MultipleFieldsForm.apply)(MultipleFieldsForm.unapply)
)

def unapply(
multipleFieldsForm: MultipleFieldsForm
): Option[(Int, String, Boolean, String, Date, UUID, String, String)] = {
Some(
(
multipleFieldsForm.i,
multipleFieldsForm.pwd,
multipleFieldsForm.active,
multipleFieldsForm.msg,
multipleFieldsForm.date,
multipleFieldsForm.uuid,
multipleFieldsForm.favMovie,
multipleFieldsForm.favDrink
)
)
}

object Movies {
val list = List(
"pulpFiction" -> "Pulp Fiction",
"inception" -> "Inception",
"theMatrix" -> "The Matrix",
"titanic" -> "Titanic"
)
}

object Drinks {
val list = List(
"vodka" -> "Vodka",
"tequila" -> "Tequila",
"whisky" -> "Whisky",
"wine" -> "Wine",
"beer" -> "Beer"
)
}

}

case class ComplexFormCustomField(
i: Int,
active: Boolean,
msg: String,
measurement: UnitMeasurement
)
object ComplexFormCustomField {

implicit val complexFormWrites: Writes[ComplexFormCustomField] =
Json.writes[ComplexFormCustomField]
val form: Form[ComplexFormCustomField] = Form(
mapping(
"i" -> number,
"active" -> boolean,
"msg" -> text,
"measurement" -> Measure.unitMeasurementMapping
)(ComplexFormCustomField.apply)(ComplexFormCustomField.unapply)
)

def unapply(
complexForm: ComplexFormCustomField
): Option[(Int, Boolean, String, UnitMeasurement)] = {
Some(
(
complexForm.i,
complexForm.active,
complexForm.msg,
complexForm.measurement
)
)
}
}

case class InputFormWithConstraints(
i: Int,
msg: String,
msgOpt: Option[String],
email: String,
birthday: Date
)
object InputFormWithConstraints {

implicit val inputFormWrites: Writes[InputFormWithConstraints] =
Json.writes[InputFormWithConstraints]
val form: Form[InputFormWithConstraints] = Form(
mapping(
"i" -> number(min = 10, max = 20),
"msg" -> text(minLength = 3, maxLength = 12),
"msgOpt" -> optional(text),
"email" -> email,
"birthday" -> date
)(InputFormWithConstraints.apply)(InputFormWithConstraints.unapply)
)

def unapply(
inputForm: InputFormWithConstraints
): Option[(Int, String, Option[String], String, Date)] = {
Some(
(
inputForm.i,
inputForm.msg,
inputForm.msgOpt,
inputForm.email,
inputForm.birthday
)
)
}
}

case class InputFormWithCustomConstraints(email: String, birthday: Date)
object InputFormWithCustomConstraints {

implicit val inputFormWrites: Writes[InputFormWithCustomConstraints] =
Json.writes[InputFormWithCustomConstraints]
val form: Form[InputFormWithCustomConstraints] = Form(
mapping(
"email" -> email.verifying(minLength(15)),
"birthday" -> date.verifying(
"Not 18 years old",
d => d.toInstant.isBefore(Instant.now().minus(18, ChronoUnit.YEARS))
)
)(InputFormWithCustomConstraints.apply)(
InputFormWithCustomConstraints.unapply
)
)

def unapply(
inputForm: InputFormWithCustomConstraints
): Option[(String, Date)] = {
Some((inputForm.email, inputForm.birthday))
}
}
49 changes: 49 additions & 0 deletions play-scala/play-templates/app/models/Measure.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package models

import play.api.data.format.{Formats, Formatter}
import play.api.data.{FormError, Forms, Mapping}
import play.api.libs.json.{Json, Writes}

object Measure {

def unitMeasurementMapping: Mapping[UnitMeasurement] =
Forms.of[UnitMeasurement]

case class UnitMeasurement(quantity: Int, unit: String)

implicit def binder: Formatter[UnitMeasurement] =
new Formatter[UnitMeasurement] {
override def bind(
key: String,
data: Map[String, String]
): Either[Seq[FormError], UnitMeasurement] = Formats.parsing(
d => UnitMeasurement.fromString(d),
"The format is (\\d*)(\\s)(\\D*)- example: \"1 pound\"",
Nil
)(key, data)

override def unbind(
key: String,
value: UnitMeasurement
): Map[String, String] = Map(key -> s"${value.quantity} ${value.unit}")
}

object UnitMeasurement {

implicit val unitMeasurementFormWrites: Writes[UnitMeasurement] =
Json.writes[UnitMeasurement]

private val pattern = "(\\d*)(\\s)(\\D*)".r

def fromString(str: String): UnitMeasurement = {
val matches = pattern.findAllIn(str)
if (matches.hasNext) {
val List(number, space, quantity) = matches.subgroups
UnitMeasurement(number.toInt, quantity)
} else {
throw new RuntimeException(s"Incorrect data: $str")
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@import helper._

@(complexFormCustomField: Form[ComplexFormCustomField])(implicit messages: Messages)


@form(action = routes.FormController.complexFormPostWithErrors()) {
@inputText(complexFormCustomField("i"))
@inputText(complexFormCustomField("active"))
@inputText(complexFormCustomField("msg"))
@inputText(complexFormCustomField("measurement"))
<input type="submit" value="Submit">
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import helper._

@(simpleForm: Form[SimpleForm])(implicit messages: Messages)

@form(action = routes.FormController.formPost()) {
@inputText(simpleForm("i"))
@inputText(simpleForm("active"))
@inputText(simpleForm("msg"))
<input type="submit" value="Submit">
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@import helper._

@(formWithConstraints: Form[InputFormWithConstraints])(implicit messages: Messages)


@form(action = routes.FormController.formWithConstraintsPost()) {
@inputText(formWithConstraints("i"))
@inputText(formWithConstraints("msg"))
@inputText(formWithConstraints("msgOpt"))
@inputText(formWithConstraints("email"))
@inputDate(formWithConstraints("birthday"))
<input type="submit" value="Submit">
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import helper._

@(simpleForm: Form[SimpleForm])(implicit messages: Messages)


@form(action = routes.FormController.formPost()) {
@inputText(simpleForm("i"))
@inputText(simpleForm("active"))
@inputText(simpleForm("msg"))
<input type="submit" value="Submit">
}
Loading

0 comments on commit 1f1b982

Please sign in to comment.