Scala, create a form with more than 22 fields - forms

I'm using scala and Play framework and i want create a form with more than 22 fields so I share my field in 3 tuple like that:
val firstMapping = tuple(
"f1" -> text, "f2" -> text, ... "f18" -> text
)
val secondMapping = tuple(
"f19" -> text, "20"-> text ... "f25" -> text
)
val thirdMapping = tuple(
"f26" -> text, ... "f29" -> text
)
So after I regroup them in a form:
val createForm = From(tuple(
"general" -> firstMapping,
"specific" -> secondMapping,
"more_specific" -> thirdMapping
))
I think this is the good solution, but my question is about the view file (i'm in a MVC architecture)
In that view I want to pass my form like that:
#(formCreate: Form[])
But I don't know what I need to put in the " [] " (I'm french I don't know wath is the word for that in english) and how to create my field in HTML ?
Usually I use that kind html form:
#helper.form() {
<input type="text" name="id_metier" id="id_metier" maxlength="255"/>
}
So can I use that kind of field again or I need to use specific field from Play framework ? And what are the parameter for that #(formCreate: Form[]) ?
Thank you for your help

Your form is of type Tuple3 with some other tuples inside. Painful to read, write, use, maintain.
Form[((String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String), (String, String, String, String, String, String, String), (String, String, String, String))]
Refer to the docs: https://www.playframework.com/documentation/2.5.x/ScalaForms
and just create a case class that will contain 3 nested case classes for your data, name fields appropriately.
Here is the example from docs for nested case class
case class AddressData(street: String, city: String)
case class UserAddressData(name: String, address: AddressData)
val userFormNested: Form[UserAddressData] = Form(
mapping(
"name" -> text,
"address" -> mapping(
"street" -> text,
"city" -> text
)(AddressData.apply)(AddressData.unapply)
)(UserAddressData.apply)(UserAddressData.unapply)
)
When creating form you refer to nested fields with . notation
#helper.inputText(userFormNested("name"))
#helper.inputText(userFormNested("address.street"))
#helper.inputText(userFormNested("address.city"))

Related

Validate scala forms, when form contains inner case classes

Help with Scala forms validation,
Here is the case class for the form data:
case class Data(
firstName: String,
lastName: String,
email: String,
confirm_email: String,
password: String,
confirm_password: String)
}
And the Scala Form:
val form = Form(
mapping(
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText,
"email" -> email,
"confirm_email" -> email,
"password" -> nonEmptyText(minLength = 8),
"confirm_password" -> nonEmptyText(minLength = 8))(Data.apply)(Data.unapply))
Now the problem is we need to validate the "email" and "confirm" email, but the problem is we need to create tuples or mapping. And so what is the best way to handle these kinds of form validation situations. It can be easily done by only using tuples and not mapping it to any case class.
But what can be done if we are requried to use mapping and case classes in forms.
First things first, I'd get rid of the confirm_email and confirm_password fields since they're redundant in the Data model. After this operation, it'll look like this:
case class Data(
firstName: String,
lastName: String,
email: String,
password: String)
Next, your form mapping needs to be updated:
val form = Form[Data](
mapping(
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText,
"email" -> tuple(
"email1" -> email,
"email2" -> email
).verifying(Messages("form.error.emailNotEquals"), email => email._1 == email._2),
"password" -> tuple(
"pass1" -> nonEmptyText(minLength = 8),
"pass2" -> nonEmptyText(minLength = 8)
).verifying(Messages("form.error.passwordNotEquals"), password => password._1 == password._2)
)((firstName, lastName, email, password) => Data(firstName, lastName, email._1, password._1))
((form: Data) => Some((form.firstName, form.lastName, (form.email, form.email), ("", ""))))
)
Two changes are required:
Nested mapping with validation both for email and password fields.
Custom apply and unapply method implementation in order to map the form with six fields into the models case class with four fields.
Notice that the custom unapply method doesn't set values for password fields since it's a desired behaviour in virtually all cases.
Finally, your view must be altered to refer new form tuple mapping correctly. For instance, fields for email should look as follows:
#helper.inputText(dataForm("email.email1"))
#helper.inputText(dataForm("email.email2"))
Fields which don't use new tuple mappings stay unchanged.

play framework how to reuse and extend a form mapping

I have a form mapping such as the following:
val myBaseMapping = mapping(
"email" -> email,
"password" -> text.verifying("Please provide a password", !_.isEmpty)
)(BaseModel.apply)(BaseModel.unapply)
This represents FormA. I have another form, FormB, that is identical but adds a couple more fields. This is what its mapping would look like:
val myExtendedMapping = mapping(
"email" -> email,
"password" -> text.verifying("Please provide a password", !_.isEmpty)
"name" -> text,
"website" -> text
)(ChildModel.apply)(ChildModel.unapply)
ChildModel extends BaseModel: it adds 2 new fields, name and website.
I am trying to code myExtendedMapping in such a way that I don't have to duplicate the binding definitions for the shared fields (email and password).
I am not sure what the Scala syntax would be here. I do not know how to 'extend' a given mapping and add bindings to it. Also, I'd prefer not to add ad-hoc verification because of the difference in behavior. Is this possible or do I just have to duplicate code?
What you can do is a simple composition. For example:
case class BaseModel(email: String, password: String)
case class ChildModel(name: String, website: String, base: BaseModel)
val commonMapping = mapping(
"email" -> email,
"passwod" -> texttext.verifying("Please provide a password", !_.isEmpty)
)(BaseModel.apply)(BaseModel.unapply)
val myExtendedForm = Form[ChildModel](mapping(
"name" -> text,
"website" -> text,
"base" -> commonMapping
)
((name, website, base) => ChildModel(name, website, base)) //bind
(child => Some(child.name, child.website, child.base)) //unbind
)

Clarifications on how to map complex objects using Play 2.1 form bindings

This is the example taken from the documentation:
import play.api.data._
import play.api.data.Forms._
case class User(name: String, age: Int)
val userForm = Form(
mapping(
"name" -> text,
"age" -> number
)(User.apply)(User.unapply)
)
val anyData = Map("name" -> "bob", "age" -> "18")
val user: User = userForm.bind(anyData).get
What is the Map instance (named anyData) doing here? I mean...is it used as a means of providing a default value for the user (in case the mapping done by the form fails)? or does it have any other purposes?
The anyData is just showing how the Map must be filled in order to be processed by the userForm and return the result value tuple (String,Int) with name and age.
The form generates a tuple from a Map and these lines just show how to do it.
val anyData = Map("name" -> "bob", "age" -> "18")
val user: User = userForm.bind(anyData).get
On a real application you will get the map implicitly from the request which contains the data filled in the HTML form by executing:
val user: User = loginForm.bindFromRequest.get

Play Forms from case classes

I'm new to playframework. I have my model for a User as well as the accompanying object for the static methods...
case class User(id: Int, username: String, password: String, fullname: String, /
lastLogin : Date, updatedOn: Date, updatedBy: Int, createdOn: Date, createdBy: Int)
I want to create a form for this class omitting some of the details. Currently I have a UserForm case class
case class UserForm(fullName:String, username: String, password:String, confirm:String)
To allow me to use:
val userForm = Form(
mapping(
"fullName" -> of[String],
"username" -> of[String],
"password" -> of[String],
"confirm" -> of[String]
)(UserForm.apply)(UserForm.unapply)
)
This feels kind of Hacker-ish. Is there an idiomatic and more conscice way to do this?
How about
val userForm = Form(
mapping(
"fullName" -> text,
"username" -> text,
"password" -> text,
"confirm" -> text
)(UserForm.apply)(UserForm.unapply)
)
There are a lot more built-in checks and validations. The basics are listed here: http://www.playframework.com/documentation/2.1.0/ScalaForms
If you don't need them in an object you could use a tuple
val userForm = Form(
tuple(
"fullName" -> text,
"username" -> text,
"password" -> text,
"confirm" -> text
)
)
The tuple in your case you have the following type: (String, String, String, String) which you could use like this: val (fullName, username, password, confirm) = refToTuple
Late coming to this, but I just released a utility to help with this! Using your classes, your code would look like this:
case class User(id: Int, username: String, password: String, fullname: String, lastLogin : Date, updatedOn: Date, updatedBy: Int, createdOn: Date, createdBy: Int)
object User { implicit val mapping = CaseClassMapping.mapping[User] }
val userForm = Form(implicitly[Mapping[User]])
You can find the source and instructions for including it in your project on github: https://github.com/Iterable/iterable-play-utils

Play 2 - Scala - Forms Validators and radio buttons

I'm need render a Form with validators for this model:
Model:
case class Service (
name: String, description: String, unitcost: Long,
typo: Char, isactive: Char, modifiedby: String)
Controller:
import play.api.data.Form
import play.api.data._
import play.api.data.format.Formats._
import play.api.data.Forms._
object Services extends Controller {
....
....
private val servicesForm[Service] = Form(
mapping(
"name" -> nonEmptyText.verifying(
"validation.name.duplicate", Service.findByName(_).isEmpty),
"description" -> nonEmptyText,
"unitcost" -> longNumber,
"typo" -> of[Char],
"isactive" -> of[Char],
"modifiedby" -> nonEmptyText
) (Service.apply)(Service.unapply)
)
This code fails on every of[Char] saying that its needed import play.api.data.format.Formats._
but I was..
My second doubt is about how to put a radio button pair for each (typo and isactive)
thinking that typo has "M" and "A" like options and isactive has "Y" and "N".
PD: I think put this using a persistence model after...
The error indicates that the Form does not know how to handle the Char type. There is no default defined for the Char type.
To solve the problem you have two options:
Change the type from Char to String for which a default Formatter exists
Supply a Formatter for Char
A formatter would look something like this (note that it does not have the correct error handling)
implicit val charFormat = new Formatter[Char] {
def bind(key: String, data: Map[String, String]):Either[Seq[FormError], Char] =
data.get(key)
.filter(_.length == 1)
.map(_.head)
.toRight(Seq(FormError(key, "error.required", Nil)))
def unbind(key: String, value: Char) = Map(key -> value.toString)
}