I just started with Play and I'm trying to create a form with an "optional checkbox". The user must agree to the general terms (don't share your login credentials, ...). The second checkbox is optional and the user can decide whether this hint should be shown in the future again when he logs in the next time:
I created some form validation code in my Scala controller:
case class AbuseStatus(abuseHintAccepted: Boolean, dismissAbuseHintInFuture: Boolean)
val abuseHintForm = Form(
mapping(
"abuseHintAccepted" -> checked("Please accept the general terms."),
"dismissHintInFuture" -> boolean
)(AbuseStatus.apply)(AbuseStatus.unapply) verifying ("you must accept the general terms", result => result match {
case abuseStatus =>
{
abuseStatus.abuseHintAccepted.booleanValue()
}
})
)
I'm using the following method to handle the POST request when the form has been sent.
def sendAbuseHintForm = Action {implicit request =>
abuseHintForm.bindFromRequest.fold(
formWithErrors =>
{
Logger.info("Form did not validate")
formWithErrors.errors.map(error => Logger.info(error.messages.mkString(",")))
Ok(views.html.abuseHint(formWithErrors))
},
abuseStatus =>
{
if(abuseStatus.dismissAbuseHintInFuture)
{
Logger.info(">>>dismissHintInFuture = true")
Ok(views.html.home()).withCookies(showAbuseHintCookie)
}
else
Ok(views.html.home())
}
)
}
The form does not validate if both checkboxes are set to true. I would like it to validate when at least the first checkbox is set to true (the second is optional). How can I achieve this and what is the difference between
"abuseHintAccepted"->checked("...")
and
"dismissHintInFuture"->boolean
They both return a Mapping[Boolean].
I have tried your example using Play 2.3.8 and it does appear to validate correctly when passing these parameters:
abuseHintAccepted:true
dismissHintInFuture:true
It would be worth making sure the form field names and values (true/false) that are posted are actually correct in all of the scenarios you describe (you haven't supplied that part of the code).
The difference between boolean and checked(msg) is that checked(msg) has validation applied ensuring the value is true - it is equivalent to boolean verifying (msg, _ == true) - (see Play framework source).
Finally, the checked() validation you have for abuseHintAccepted means that the verifying check on the whole form is not needed, but this shouldn't affect the behaviour of the form (it is just duplicate validation).
Related
Play documentation mentions parse.form method which can be used to bind to an incoming request. I am using play 2.2.x. Is this method defined in this release? I am getting compilation error
value form is not a member of object controllers.Application.parse
def regSubmit = Action(parse.form(userForm) { implicit request =>
val userData= request.body
Ok(views.html.regconf("Registration Successful")(userForm.fill(userData)))
})
As far as I can tell from the 2.2.x source code, parse.form did not exist then, and was only introduced in 2.4.x.
Any reason not to use the "equivalent" bindFromRequest and deal with errors that might be present? Along the lines of:
def regSubmit = Action { implicit request =>
userForm.bindFromRequest.fold (
errors => //-- 'errors' is a form with FormErrors set
Ok(views.html.register(errors)) //-- register is the initial form
userData => //-- 'userData' is the case class that userForm maps to
Ok(views.html.regconf("Registration Successful")(userForm.fill(userData)))
)
}
I have not checked the source code to see whether it is in 2.2.x. It is not mentioned on the ScalaForms page of the docs.
I have use case where I need to read value from query string.
Currently I have two different APIs(Some other person has created the code) which maps to same URL
GET /service/class/:className/details controllers.Student.getStudentDetails(studentId)
GET /service/class/:className/details controllers.Student.getAllStudentsDetails()
If query string is present in URL then API1 should execute, otherwise API2.
As URL is same for both APIs, I am able to hit only get-student-details API(Because it has higher priority in routes file).
I am looking for alternatives to fix this problem.
As per my knowledge we don't need to create different APIs just to handle query strings.
I am thinking to merge 2 different APIs in single APIs which takes action depending upon presence of query string in request.
What I want to know is if there is way to execute two different APIs which maps to same URL(Only difference is with query string).
NOTE: I am using play 2.4.6.
I see few ways using a single controller function (say we chose getStudentDetails)
1) Having an Option parameter:
def getStudentDetails(studentId: Option[String]) = Action { studentId match {
case Some(id) => // do something
case None => // do something else
}
// ..
}
2) Look for your query string parameters inside your http request:
def getStudentDetails = Action { request =>
request.queryString.get("studentId") match {
case Some(list) => // do something...beware this is a List
case None => // do something else
}
//...
}
I'm new rather new to Scala so I think this might be a very small problem.
I'm currently trying to change the method chat from using the deprecated WebSocket.async to WebSocket.tryAccept. The application uses the sample chat found at PlayFramework websocket-chat
I'm having trouble creating the complex Future type that the method requires.
This is the old method:
def chat() = WebSocket.async[JsValue] {
request =>
ChatRoom.join("User: 1")
}
New method:
def chat2() = WebSocket.tryAccept[JsValue] {
request =>
try {
// ChatRoom.join returns (iteratee,enumerator)
ChatRoom.join("User: 1").map(e => Right(e))
} catch {
case e: Exception =>
Left(Ok("Failed")) // Error here
}
}
My error message:
found : Left[Result,Nothing]
required: Future[Either[Result,(Iteratee[JsValue, _], Enumerator[JsValue])]]
I have no idea how I am supposed to create such a complex result for such a simple message.
Although ChatRoom.join("User: 1").map(e => Right(e)) doesn't show any errors now, I'm unsure if this is the correct implementation.
I'm not in front of an IDE at the moment, so I can't answer fully, but the return type it's asking for isn't as complex as it seems. An "Either" is a "Left" or a "Right" in the same way that an "Option" is a "Some" or a "None". So what it's asking for is a Future (which Websocket.async should also have required) that contains either a Left[Result] -- the fail-to-connect case, or a Right[(Iteratee, Enumerator)] -- the success case. Assuming that Chatroom.join returns a Future[(Iteratee, Enumerator)], the map operation is simply wrapping that in a "Right". The first thing I'd try is wrapping Left(Ok("Failed")) in a Future and see what happens.
Im new to Play 2 and Scala, and im getting a strange Exception in my template:
Execution exception
-------------------
[NoSuchElementException: None.get]
In /home/nic/workspaces/scala-ide/scims/app/views/persons/detailTabs/personal.scala.html at line 4.
1. #(personId: Long, personDetailTabForm: Form[dto.PersonDetailTab])(implicit formOptions: dto.PersonFormOptions)
2. #implicitFieldConstructor = #{ helper.FieldConstructor(support.bs3HorizField.f) }
3.
4. #persons.detail("personal", personDetailTabForm.get.firstName) {
The personDetailTabForm is an empty form object defined as:
val personalDetailTabForm: Form[PersonDetailTab] = Form(
mapping(
"firstName" -> text.verifying(nonEmpty),
"middleName" -> text,
"lastName" -> text.verifying(nonEmpty),
"gender" -> text,
"dateOfBirth" -> jodaDate("yyyy-MM-dd"),
"ethnicity" -> text,
"maritalStatus" -> text,
"password" -> text
)(PersonDetailTab.apply)(PersonDetailTab.unapply)
)
Any ideas as to what's wrong here?
I was under the impression a variable would have to be an Option to get a None?
Cheers
NFV
You are calling get on personDetailTabForm - Looking up it's ScalaDoc: http://www.playframework.com/documentation/2.2.x/api/scala/index.html#play.api.data.Form - it seems that .get returns the PersonDetailTab value that the form holds - IF, as the docs say, 'the submission was a success'.
You're seeing the None.get exception because most likely play.api.data.Form[T] simply uses Option[T] and get returns Some[T] when the form holds a valid value and None otherwise.
So on your line 4, in the scala template, you have something like
personDetailTabForm.get.firstName
That's a String, but you can expect a value only when the form's underlying PersonDetailTab itself has a value. I am not sure what you want to do, but you're dealing with a case where a value you want to render in a template might not be there, for whatever reason. In which case:
#personDetailTabForm.value.map{ personDetailTab =>
#persons.detail("personal", personDetailTab.firstName) // { ... whatever else
// anything else you want to render
} getOrElse { // errors in the form; personDetailTabForm cannot yield a valid personDetailTab
<h3> oops, what went wrong here? </h2>
}
It all depends on what you want to do in personal.scala.html. Form[T] is a good way
to deal with input and validation of some T thing, but if you are just displaying it,
and if you have a T (in your case PersonDetailTab) just pass that to the template as it is. If your PersonDetailTab may or may not exist, then just use Option[PersonDetailTab] instead Form[PersonDetailTab].
ScalaForms
In the example linked here there is this example about form validation:
// You can also define ad-hoc constraints on the fields:
val loginForm = Form(
tuple(
"email" -> nonEmptyText,
"password" -> text
) verifying("Invalid user name or password", fields => fields match {
case (e, p) => User.authenticate(e,p).isDefined
})
)
Via binding errors some constraints are displayed in my form. (Like the nonEmptyText, that gets a extra line behind the field stating This field is required. See:
loginForm.bindFromRequest.fold(
formWithErrors => // binding failure, you retrieve the form containing errors,
value => // binding success, you get the actual value
)
If I do a .toString to the formWithErrors i get this for the nonEmptyText constraint:
Form(ObjectMapping2(<function2>,<function1>,(Email adress,FieldMapping(,List(Constraint(Some(constraint.required),WrappedArray())))),(Password,FieldMapping(,List(Constraint(Some(constraint.required),WrappedArray())))),,List(Constraint(None,List()))),Map(Password -> test, Email adress -> ),List(FormError(Email adress,error.required,WrappedArray())),None)
The latter part is a FormError List: List(FormError(Email adress,error.required,WrappedArray())),None) which is a case class: case class FormError (key: String, message: String, args: Seq[Any]) where key is defined as: The error key (should be associated with a field using the same key)..
The FieldConstructor picks this up and makes the 'Email adress' input box go red and add the error message ('This field is required').
Displaying the ad hoc constraints?
So when the form fields are all filled the username and password are checked:
verifying("Invalid user name or password", fields => fields match {
case (e, p) => User.authenticate(e,p).isDefined
})
But i dont know how to display the 'Invalid user name or password' to the user. The .toString of the FormError List of formWithErrors is:
List(FormError(,Invalid user name or password,WrappedArray())),None)
The Key part is empty.
Question
How do i display the ad hoc error?
I even tried:
BadRequest( views.html.test( formWithErrors ) ).flashing( "error" -> "Invalid user name or password." ) },
but for some reason its not working via the Flash either :(. This #flash.get("error").getOrElse("no 'error'") gives me 'no error' each time.
Actually, a form can have errors attached to it, so then when a bind from request fails validating constraint, a new form is created based on the given one, but filled in with errors.
Such errors list is composed of FormError which refers a Form Field and overrides toString to show its embed message.
This way if you wish to show all messages at once, you can simply formWithErrors.errors.mkString("<br>") for instance (in you template or flashing it).
There are a lot of ways and some common ones are described here http://www.playframework.org/documentation/2.0.2/ScalaFormHelpers. You might define your own field constructor as well.
To conclude, my advice would be that you should use helpers amap (for instance #inputText and co) because they already contain the logic to show helpers, tips and errors when available.
Edit
I missed the hint about the flash problem... you've probably forgotten to add (implicit flash: Flash) to you template param list. (don't forget to flag the request as implicit as well in your action definition)