I'm browsing around in the documentation but I can't find an example of how to use the inputRadioGroup in my Controller.
I guess i should use this helper. But how do I bind it to my form in my controller?
I would like to show a radio group that represents the grades 1 - 5
Controller:
object Sms extends Controller {
val testForm: Form[Test] = Form (
mapping(
"firstname" -> nonEmptyText,
"lastname" -> nonEmptyText,
"password" -> tuple(
"main" -> text(minLength = 6),
"confirm" -> text
).verifying(
"Passwords don't match", passwords => passwords._1 == passwords._2
),
"email" -> tuple(
"main" -> (text verifying pattern("^([0-9a-zA-Z]([-\\.\\w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})$".r, error="A valid email is req")),
"confirm" -> text
).verifying(
"Emails don't match", emails => emails._1 == emails._2
),
"grade" -> Do the magic happen here?
)(Test.apply)(Test.unapply)
)
case class Test(
firstname: String,
lastname: String,
password: String,
email: String,
grade: Int
)
}
view:
#inputRadioGroup(
testForm("grade"),
options = Seq("1"->1,"2"->2....and so on)
'_label -> "Grade",
'_error -> testForm("grade").error.map(_.withMessage("some error")))
I can't figure out how to do this.
In your controller you create a Seq of the possible grades and pass the Seq to your view. I like using a case class Grade better then to pass a Tuple2[String, String] to the view. But I guess that's a matter of opinion.
case class Grade(value: Int, name: String)
private val grades = Seq(Grade(1, "Brilliant"), Grade(2, "Good"), Grade(3, "Ok"))
val testForm: Form[Test] = Form (...
"grade"-> number
)(Test.apply)(Test.unapply)
def edit(id: Long) = Action {
val model = ...obtain model
Ok(views.html.edit(testForm.fill(model), grades))
}
def submit() = Action {
testForm.bindFromRequest.fold(
formWithErrors => Ok(views.html.edit(formWithErrors, grades))
}, test => {
Logger.info("grade: " + grades.find(_.value == test.grade).map(_.name))
//save model...
Redirect(...
})
}
in your view, you map the grades Seq to a Tuple2[String, String] to feed inputRadioGroup
#(testForm: Form[Test], grades: Seq[Grade])
#inputRadioGroup(contactForm("grade"),
options = grades.map(g => g.value.toString -> g.name),
'_label -> "Grade")
Related
What I want to do : I try to test a action method addbook with FakeRequest like below.
val request = FakeRequest()
.withSession("email" -> "admin#admin.co.jp", "name" -> "yun", "id" -> "1")
.withFormUrlEncodedBody(
"name" -> "Great Gatsby",
"price" -> "30",
"author" -> "Scott",
"description" -> "Great classic"
)
.withCSRFToken
val result = homeController.addBook(request).run()(materializer)
flash(result).get("msg") mustBe Some("some msg")
status(result) must equal(SEE_OTHER)
redirectLocation(result) mustBe Some("/somelocation")
What is wrong : But the when I bindFromRequest the Form data, I get nothing but form constraint errors.
[warn] c.HomeController - data :
[warn] c.HomeController - errors : FormError(name,List(error.required),List()), FormError(price,List(error.required),List())
addBookForm is defined with at least two fields ("name", "price") that are required
val addBookForm = Form(
mapping(
"name" -> nonEmptyText,
"price" -> longNumber,
"author" -> optional(text),
"description" -> optional(text),
"pictures" -> Forms.list(text).verifying("more than 5 pictures detected", list => list.size <= 5),
"reserved" -> optional(boolean),
"publisher" -> optional(longNumber),
)(BookFormData.apply)(BookFormData.unapply)
)
The addbook action definition is like below.
def addBook = isAuthenticatedAsync { (userId, userName, userEmail) =>
implicit request =>
logger.warn("data : " + addBookForm.bindFromRequest.data.mkString(", "))
logger.warn("errors : " + addBookForm.bindFromRequest.errors.mkString(", "))
....
And isAuthenticatedAsync
def isAuthenticatedAsync (f: => (String, String, String) => MessagesRequest[AnyContent] => Future[Result]) = Security.Authenticated(userInfo, onUnauthorized) { user =>
Action.async(request => f(user._1,user._2,user._3)(request))
}
When I change isAuthenticatedAsync to just Async method, I can get the form data but I don't know what I'm missing, why it is not working.
Please tell me what I'm missing?
Have a great day!
EDIT
I've included that addbookForm code.
Just to emphasize, the addbook action works as expected with real request(via browser)
But when I test it with Faketest, the form data is lost
I got the answer from schmitch(Schmitt Christian).
He responded to my question that I also posted to discuss list of lightbend.
https://discuss.lightbend.com/t/fakerequest-withformurlencodedbody-data-is-lost/636/3
So the bottom line is instead of using run() method, use the method :
def call[T](action: EssentialAction, req: Request[T])(implicit w: Writeable[T], mat: Materializer): Future[Result] =
call(action, req, req.body)
The further explanation is described in the original answer in the link above.
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.
I'm brand new to Play!, and I'm trying to migrate my existing website from cakePHP to Play!.
The problem I'm facing is about form validation.
I defined a case class User, representing the users of my website :
case class User(
val id: Long,
val username: String,
val password: String,
val email: String
val created: Date)
(There are some more fields, but these ones suffice to explain my problem)
I would like my users to be able to create an account on my website, using a form, and I would like this form to be validated by Play!.
So, I created the following action :
def register = Action {
implicit request =>
val userForm = Form(
mapping(
"id" -> longNumber,
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email,
"created" -> date)(User.apply)(User.unapply))
val processedForm = userForm.bindFromRequest
processedForm.fold(hasErrors => BadRequest("Invalid submission"), success => {
Ok("Account registered.")
})
}
Obviously, I do not want the user to fill the id or the creation date by himself in the form. So my question is : what should I do ?
Should I define a new "transition model" containing only the fields that are actually provided to the user in the form, and transform this intermediary model into the complete one before inserting it into my database ?
That is, replace my action by something like that :
def register = Action {
implicit request =>
case class UserRegister(
username: String,
password: String,
email: String)
val userForm = Form(
mapping(
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(8),
"email" -> email)(UserRegister.apply)(UserRegister.unapply)
val processedForm = userForm.bindFromRequest
processedForm.fold(hasErrors => BadRequest("Invalid submission"), success => {
val user = User(nextID, success.username, success.password, success.email, new Date())
// Register the user...
Ok("Account created")
}
Or is there another, cleaner way to do what I want ?
I've been through many tutorials and the book "Play for Scala", but in the only examples that I found, the models were fully filled by the forms... I really like Play! so far, but it looks like the documentation often lacks examples...
Thank you very much for your answers !
You have a few choices:
Firstly, you could make the id and created fields Option[Long] and Option[Date] respectively. Then use a mapping like:
val userForm = Form(
mapping(
"id" -> optional(longNumber),
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email,
"created" -> optional(date)
)(User.apply)(User.unapply)
)
That would, I think, be logical, since a User with a None id would indicate that it has not yet been saved. This works well when you want to use the same form mapping to update an existing record.
Alternately, you can use ignored mappings with some arbitrary placeholder data:
val userForm = Form(
mapping(
"id" -> ignored(-1L),
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email,
"created" -> ignored(new Date)
)(User.apply)(User.unapply)
)
This is not so good when reusing the form for update operations!
Finally, don't forget that your form mappings are bound/filled by functions that turn a tuple into an object, and an object into a tuple respectively. It's just a convenient convention to use the case class User.apply and User.unapply methods since these do just that. You could write alternative factory methods on your User object to handle form instantiation:
object User {
def formApply(username: String, password: String, email: String): User =
new User(-1L, username, password, email, new Date)
def formUnapply(user: User): Option[(String,String,String)] =
Some((user.username, user.password, user.email))
}
And then user those in the Form object:
val userForm = Form(
mapping(
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email
)(User.formApply)(User.formUnapply)
)
Also, it's worth noting that the Scala forms documentation is about to get much better in 2.2.1 (and in fact it might already have rolled out here).
I'm creating a scala application using Play framework and mongoDB. I manage to have the connections up using Leon Play-Salat. I have a model
case class Person(
id: ObjectId = new ObjectId,
fname: String,
mname: String,
lname: String
)
In my controller I need to map it to a form
val personForm: Form[Person] = Form(
// Defines a mapping that will handle Contact values
mapping(
"id" -> of[ObjectId],
"fname" -> nonEmptyText,
"mname" -> text,
"lname" -> nonEmptyText
)(Person.apply)(Person.unapply))
How do I map the ObjectID to the form ? I'm getting error Object not found for the ObjectId.
Manage to get it working
val personForm: Form[Person] = Form(
// Defines a mapping that will handle Contact values
mapping(
"id" -> ignored(new ObjectId),
"fname" -> nonEmptyText,
"mname" -> text,
"lname" -> nonEmptyText
)(Person.apply)(Person.unapply))
I'm trying to do a CRUD function thus need the ID.
Found using own constructor and deconstructor is better
val personForm: Form[Person] = Form(
mapping(
"fname" -> nonEmptyText,
"mname" -> text,
"lname" -> nonEmptyText
)((fname, mname, lname) => Person(new ObjectId, fname, mname, lname))
((person: Person) => Some((person.fname, person.mname, person.lname))) )
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.