scala play form how handle java bigdecimal - scala

I try to fill a form with the values of System
def edit(id: Long) = IsAuthenticated { username => implicit request =>
User.findByEmail(username).map { user =>
System.findById(id).map { system =>
Ok(html.systems.editForm(id, systemForm.fill(system), Person.options, user))
}.getOrElse(NotFound)
}.getOrElse(Forbidden)
}
but some of 'system' values are java.math.BigDecimal
val systemForm = Form(
mapping(
"id" -> ignored(NotAssigned:Pk[BigDecimal]),
"sys_name" -> nonEmptyText,
"sys_desc" -> nonEmptyText,
"sys_owner1_id" -> longNumber,
"sys_owner2_id" -> optional(longNumber)
)(System.apply)(System.unapply)
)
and it says :
type mismatch; found : (anorm.Pk[java.math.BigDecimal], String, String, String, Option[String], java.math.BigDecimal, Option[java.math.BigDecimal]) => models.System required: (anorm.Pk[java.math.BigDecimal], String, String, Long, Option[Long]) => ?
how can i handle this?

Looks like problem in anorm or somewhere near.
As you may see from error description sys_owner1_id and sys_owner2_id are BigIntegers in query result, but declared as long in form.
I am not familiar with anorm, but solution is to declare these ids as long in anorm, or declare them as BigInteger in form mapping or convert from BigInteger to long in your query.

Related

Converting Pk[Long] to Option[Long] in a Form

I have been having trouble understanding what the issue is here since the Scala Anorm Pk became deprecated.
I switched my model to the following:
case class Item(id: Option[Long] = NotAssigned,
title: String,
descr: String,
created: Option[Date],
private val imgs: List[Img],
private val tags: List[Tag])
From id: Pk[Long]
I changed my form to:
val itemForm = Form(
mapping(
"id" -> ignored(23L),
"title" -> nonEmptyText,
"descr" -> nonEmptyText,
"created" -> optional(ignored(new Date)),
"imgs" -> Forms.list(itemImgs),
"tags" -> Forms.list(itemTags)
)(Item.apply)(Item.unapply)
)
From "id" -> ignored(NotAssigned:Pk[Long])
But, I get this error.
type mismatch; found : (Option[Long], String, String, scala.math.BigDecimal, Option[java.util.Date], List[models.Img], List[models.Tag]) => models.Item required: (Long, String, String, Option[java.util.Date], List[models.Img], List[models.Tag]) => ?
)(Item.apply)(Item.unapply)
Why is an Option[Long] not required on the Item model?
I don't know what 23L is, but that's what was in the Play Documentation. The value of id in the database is coming from a sequence.
If I change it to:
"id" -> ignored(NotAssigned:Option[Long]),
Which makes the most sense to me... I get this error:
type mismatch; found : anorm.NotAssigned.type required: Option[Long]
"id" -> ignored(NotAssigned:Option[Long]),
Which makes less sense than before.
Just to clarify, it's not Anorm that's deprecated, but the Pk type within Anorm.
Your problem here is that you're trying to assign NotAssigned to an Option[Long], which is incompatible. You should change all of the NotAssigneds to None.
So your class would look like this:
case class Item(
id: Option[Long] = None,
title: String,
descr: String,
price: BigDecimal,
created: Option[Date],
private val imgs: List[Img],
private val tags: List[Tag]
)
And the Form mapping:
"id" -> ignored[Option[Long]](None)

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)
}

Play: Bind a form field to a double?

Perhaps I'm just overlooking something obvious but I can't figure out how to bind a form field to a double in a Play controller.
For instance, assume this is my model:
case class SavingsGoal(
timeframeInMonths: Option[Int],
amount: Double,
name: String
)
(Ignore that I'm using a double for money, I know that's a bad idea, this is just a simplified example)
And I wanted to bind it like so:
object SavingsGoals extends Controller {
val savingsForm: Form[SavingsGoal] = Form(
mapping(
"timeframeInMonths" -> optional(number.verifying(min(0))),
"amount" -> of[Double],
"name" -> nonEmptyText
)(SavingsGoal.apply)(SavingsGoal.unapply)
)
}
I realized that the number helper only works for ints but I thought using of[] might allow me to bind a double. However, I get a compiler error on this:
Cannot find Formatter type class for Double. Perhaps you will need to import
play.api.data.format.Formats._
Doing so doesn't help as there's no double formatter defined in the API.
This is all just a long way of asking what's the canonical way of binding a form field to a double in Play?
Thanks!
edit: 4e6 pointed me in the right direction. Here's what I did using his help:
Using the snippets in his link, I added the following to app.controllers.Global.scala:
object Global {
/**
* Default formatter for the `Double` type.
*/
implicit def doubleFormat: Formatter[Double] = new Formatter[Double] {
override val format = Some("format.real", Nil)
def bind(key: String, data: Map[String, String]) =
parsing(_.toDouble, "error.real", Nil)(key, data)
def unbind(key: String, value: Double) = Map(key -> value.toString)
}
/**
* Helper for formatters binders
* #param parse Function parsing a String value into a T value, throwing an exception in case of failure
* #param error Error to set in case of parsing failure
* #param key Key name of the field to parse
* #param data Field data
*/
private def parsing[T](parse: String => T, errMsg: String, errArgs: Seq[Any])(key: String, data: Map[String, String]): Either[Seq[FormError], T] = {
stringFormat.bind(key, data).right.flatMap { s =>
util.control.Exception.allCatch[T]
.either(parse(s))
.left.map(e => Seq(FormError(key, errMsg, errArgs)))
}
}
}
Then, in my form mapping:
mapping(
"amount" -> of(Global.doubleFormat)
)
You don't need to use the the format in the global if you have the version 2.1 up.
Just import:
import play.api.data.format.Formats._
and use as:
mapping(
"amount" -> of(doubleFormat)
)
Actually, there is predefined formatter for Double on master branch. So you should either switch to 2.1-SNAPSHOT play version or just copy the implementation.

Play! framework 2.0: Validate field in forms using other fields

In the play! framework, using scala,say that i have a form such as follows:
import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints._
case class User(someStringField: String, someIntField: Int)
val userForm = Form(
mapping(
"someStringField" -> text,
"someIntField" -> number verifying(x => SomeMethodThatReceivesAnIntAndReturnsABoolean(x))
)(User.apply)(User.unapply)
)
where SomeMethodThatReceivesAnIntAndReturnsABoolean is a method that performs some logic on the int to validate it.
However, i would like to be able to consider the value of the someStringField when validating the someIntField, is there a way to achieve this in play framework's forms? I know that i can do something like:
val userForm = Form(
mapping(
"someStringField" -> text,
"someIntField" -> number
)(User.apply)(User.unapply)
.verifying(x => SomeFunctionThatReceivesAnUserAndReturnsABoolean(x))
and then i would have the entire user instance available passed to the validation function. The problem with that approach is that the resulting error would be associated with the entire form instead of being associated with the someIntField field.
Is there a way to get both things, validate a field using another field and maintain the error associated to the specific field i wish to validate, instead of the entire form?
I have the same requirements with adding validation to fields depending on the value of other fields. I´m not sure how this is done in idiomatic PLAY 2.2.1 but I came up with the following solution. In this usage I´m degrading the builtin "mapping" into a simple type converter and apply my "advanced inter field" validation in the "validateForm" method. The mapping:
val userForm = Form(
mapping(
"id" -> optional(longNumber),
"surename" -> text,
"forename" -> text,
"username" -> text,
"age" -> number
)(User.apply)(User.unapply)
)
private def validateForm(form:Form[User]) = {
if(form("username").value.get == "tom" || form("age").value.get == "38") {
form
.withError("forename", "tom - forename error")
.withError("surename", "tom - surename error")
}
else
form
}
def update = Action { implicit request =>
userForm.bindFromRequest.fold({
formWithErrors => BadRequest(users.edit(validateForm(formWithErrors)))
}, { user =>
val theForm = validateForm(userForm.fill(user))
if(theForm.hasErrors) {
BadRequest(users.edit(theForm))
} else {
Users.update(user)
Redirect(routes.UsersController.index).flashing("notice" -> s"${user.forename} updated!")
}
})
}
Even though it works I´m urgently searching for a more idiomatic version...
EDIT: Use a custom play.api.data.format.Formatter in idiomatic play, more on http://workwithplay.com/blog/2013/07/10/advanced-forms-techniques/ - this lets you programmatically add errors to a form. My Formatter looks like this:
val usernameFormatter = new Formatter[String] {
override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], String] = {
// "data" lets you access all form data values
val age = data.get("age").get
val username = data.get("username").get
if(age == "66") {
Left(List(FormError("username", "invalid"), FormError("forename", "invalid")))
} else {
Right(username)
}
}
override def unbind(key: String, value: String): Map[String, String] = {
Map(key -> value)
}
}
}
Registered in the form mapping like this:
mapping(
[...]
"username" -> of(usernameFormatter),
[....]
I believe what you're looking for is play.api.data.validation.Constraint.
Say you have a RegisterForm with a list of predefined cities and an otherCity field and you need either the cities or otherCity to be supplied, i.e., otherCity should be validated if cities is not provided:
case class RegisterForm(
email: String,
password: String,
cities: Option[List[String]],
otherCity: Option[String]
)
You can write a custom Constraint around this:
val citiesCheckConstraint: Constraint[RegisterForm] = Constraint("constraints.citiescheck")({
registerForm =>
// you have access to all the fields in the form here and can
// write complex logic here
if (registerForm.cities.isDefined || registerForm.otherCity.isDefined) {
Valid
} else {
Invalid(Seq(ValidationError("City must be selected")))
}
})
And your form definition becomes:
val registerForm = Form(
mapping(
"email" -> nonEmptyText.verifying(emailCheckConstraint),
"password" -> nonEmptyText.verifying(passwordCheckConstraint),
"cities" -> optional(list(text)),
"other_city" -> optional(text)
)(RegisterForm.apply)(RegisterForm.unapply).verifying(citiesCheckConstraint)
)
In this example emailCheckConstraint and passwordCheckConstraint are additional custom constraints that I defined similar to citiesCheckConstraint. This works in Play 2.2.x.
UPDATE:
Works on Play 2.3.8 as well.
if you don't mind having a prefix for you params you can group the related params:
val aForm = Form(
mapping(
"prefix" -> tuple(
"someStringField" -> text,
"someIntField" -> number
) verifying (tup => your verification)
)(tup => User.apply(tup._1, tup._2)(User.unapply...)
I use something similar just without the surrounding mapping.
You will have to adjust the apply/unapply a little and pass the arguments manually for it to compile.
The error will be registered to the "prefix" group.
I also find it weird that you cannot register errors on any field you'd like using FormError when verifying the form...
Thanks to Tom Myer, Here what I used
class MatchConstraint[A](val targetField:String, val map:(String, Map[String, String]) => A, val unmap:A => String) extends Formatter[A] {
override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], A] = {
val first = data.getOrElse(key, "")
val second = data.getOrElse(targetField, "")
if (first == "" || !first.equals(second)) {
Left(List(FormError(key, "Not Match!")))
}
else {
Right(map(key, data))
}
}
override def unbind(key: String, value: A): Map[String, String] = Map(key -> unmap(value))
}
And here what's my form look like
val registerForm = Form(
mapping(
"email" -> email.verifying(minLength(6)),
"password" -> text(minLength = 6),
"passwordConfirmation" -> of(new MatchConstraint[String]("password", (key, data) => data.getOrElse(key, ""), str => str))
)(RegisterData.apply)(RegisterData.unapply)
)
I guess that they map the scala-code to JSR-Validation. There it's definitely not possible. There are some arguments to do this. Mainly that a validation should be simple and not make complex logic. How ever I still miss this too. OVal from play1 was better for me.
In the documentation:
Playframework Documentation
You can see the following code:
val userFormConstraintsAdHoc = Form(
mapping(
"name" -> text,
"age" -> number
)(UserData.apply)(UserData.unapply) verifying("Failed form constraints!", fields => fields match {
case userData => validate(userData.name, userData.age).isDefined
})
)
Mainly just verify after the unapply and you have all the fields mapped, so you can make a more complete validation.

Scala, Lift | SHtml.select - how to put list from database

I'm trying to put into form ( select ) some values from database:
val kateg = Kategoria.findAll.map(a => (a.id.toString , a.nazwa))
And next in form:
bind("entry", xhtml,
"kateg" -> SHtml.select(kateg, Empty, select ),
"temat" -> SHtml.text(temat, temat = _),
"opis" -> SHtml.textarea(opis, opis = _, "cols" -> "80", "rows" -> "8"),
"submit" -> SHtml.submit("Add", processEntryAdd))
And then i have error:
Description Resource Path Location Type
type mismatch; found : List[(java.lang.String, a.nazwa.type) for
Some { val a: code.model.Kategoria }]
required: Seq[(String, String)] Forma.scala
/lift-todo-mongo/src/main/scala/code/snippet
line 51 Scala Problem
any ideas ? Thanks
SHtml.select(..) allows you to choose a String value.
It takes a Seq of tuples (Value: String, Key: String)
In that case you probably need to write:
val kateg = Kategoria.findAll.map(a => (a.id.toString , a.nazwa.is))
if nazwa is MappedString field of Kategoria entity.
i.e. kateg should have a type of Seq[(String, String)]
But I would suggest you to use SHtml.selectObj to select Kategoria entity instead of String name value:
val kateg: Seq[(Kategoria, String)] = Kategoria.findAll.map(a => (a, a.nazwa.is))
SHtml.selectObj[Kategoria](kateg, Empty, (k: Kategoria) => { .. /* assign */ .. })