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)
)
Actually, there is predefined formatter for
Double
on master branch. So you should either switch to2.1-SNAPSHOT
play version or just copy the implementation.You don't need to use the the format in the global if you have the version 2.1 up.
Just import:
and use as: