PlayFramework 2.x - Forms / Associating error message to one element of a tuple - forms

I've got this form mapping:
val myElement = Form(
mapping(
"title" -> nonEmptyText,
"schedule" ->
tuple("startSchedule" -> jodaDate("dd/MM/yyyy HH:mm"),
"endSchedule" -> jodaDate("dd/MM/yyyy HH:mm"))
.verifying(MyValidator().checkForEndScheduleConsistency("error.schedule")),
)(MyElement.apply)(MyElement.unapply)
)
MyElement class:
case class MyElement(title: String, schedule: (Datetime, Datetime))
MyValidator class:
def checkForEndScheduleConsistency(errorMsg: String) =
Constraint[(DateTime, DateTime)]("constraint.schedule", errorMsg) {
schedule =>
MyDomainValidator().checkForEndScheduleConsistency(schedule._1, schedule._2, Messages(errorMsg)) match {
case Success(s) => Valid
case Failure(f) => Invalid(ValidationError("custom error string from `f`"))
}
}
Requirement: An error message must be associated to the field schedule.endSchedule(tuple's element) if the schedule is inconsistent according to MyValidator object.
However, in order to have both required elements (startSchedule and endSchedule) available for checkForEndScheduleConsistency method, I can't apply a verifying method directly on the nested tuple's element named endSchedule. Instead, I have to apply one on the whole tuple in order to include startSchedule variable, as shown in the code snippet.
The drawback is that the error is not mapped to endSchedule but to schedule (that doesn't represent anything in my HTML form), and so nothing is displayed to screen when an inconsistent schedule appears.
Therefore, I have to use this "workaround" to achieve my requirement using Form's withError method:
def create = Action {
implicit request =>
myForm.bindFromRequest.fold(
myFormWithErrors => {
myFormWithErrors.error("schedule") match { //check for the presence of potential schedule error
case Some(e) => {
BadRequest(views.html.create_element("Create an element", myFormWithErrors.withError("schedule.endSchedule", Messages("error.schedule"))))
}
case _ => BadRequest(views.html.create_element("Create an element", myFormWithErrors))
}
},
myForm => {
treatSubmittedMyForm(myForm)
}
)
}
=> Very ugly and anti-DRY.
Is there a way to apply verifying on the tuple and despite of that, apply the error message to a nested tuple's element? In my case, on endSchedule.

Probably the best solution is to replace the default helpers provided by Play when rendering the field by one of your own making, as per documentation:
#(elements: helper.FieldElements)
<div class="#if(elements.hasErrors) {error}">
<label for="#elements.id">#elements.label</label>
<div class="input">
#elements.input
<span class="errors">#elements.errors.mkString(", ")</span>
<span class="help">#elements.infos.mkString(", ")</span>
</div>
</div>
That way you can manually replace the error reference to the right element. Not extremely nice but you should be able to create a reusable tag from it.

The best solution is to precise the global (to schedule) "error" key into the more specific generated input directly, here the schedule.endSchedule input:
#inputText(hobbyForm("schedule.endSchedule"), 'id -> "schedule.endSchedule", '_error -> hobbyForm.error("schedule")
Notice the part: '_error -> hobbyForm.error("schedule")

Related

how to make mapping function work if the incoming request has less fields than expected

I am experimenting with play/scala. I have following two case classes and I want to map data from form into this model
case class User (
name:String,
age:Int,
female:Boolean,
address:Address
)
case class Address (
fullStreet:String,
county:String,
country:String
)
In controller class, I have following mapping function and action defined
val userForm = Form((mapping("name"->text,
"age"->number,
"female"->boolean,
"address"->mapping("fullStreet"->text,
"county"->text,
"country"->text)(Address.apply)(Address.unapply)
)(User.apply)(User.unapply)))
def post = Action { implicit request =>
val u:Form[User] = userForm.bindFromRequest
Ok(views.html.dataIndex(u))
}
I am facing the following issue: To make the complete code work, I have to create a form which contains all the fields required in mapping as follows:
<h1>Feed User Data</h1>
#helper.form(action=routes.Data.post){
#helper.inputText(userForm("name"))
#helper.inputText(userForm("age"))
#helper.checkbox(userForm("female"))
<fieldset>
#helper.inputText(userForm("address.fullStreet"),'_label -> "Full Street")
#helper.inputText(userForm("address.county"),'_label -> "County")
#helper.select(userForm("address.country"),Seq(""->"---",
"United Kingdom"->"UK",
"France"->"FR") )
</fieldset>
<input type="submit" name="send" value="submit"/>
}
If I create a form with say only input field for name, then bindFromRequest returns None instead of mapping only name field. Is there a way in which the form can contain less fields than required in mapping. I am not talking about fields in form with empty/optional values. I do not want to put the fields in the form at all.
I usually create a case class that represents the form data (probably not all the info from the domain class), and in the controller/service I create the domain entity using my own rules (for instance, a default value for a field not represented on the form)

Play Framework Form Error Handling

This is my view file containing the form that has to filled in by the user:
#helper.form(call) {
#helper.input(resumeForm("surname"), '_label -> "Surname") { (id, name, value, args) =>
<input name="#name" type="text" value="#value" placeholder="Enter your surname">
}
}
This is my custom field constructor:
#(elements: helper.FieldElements)
#if(!elements.args.isDefinedAt('showLabel) || elements.args('showLabel) == true) {
<div class="input-with-label text-left">
<span>#elements.label</span>
#elements.input
</div>
} else {
#elements.input
}
Now I have a dilemma. When the entered value doesn't clear validation, I need to add the class field-error to the input and I need to add data-toggle, data-placement and title. However, I don't know of any way to check if there are errors for the specific field. What is the best way to implement this? I already looked at using inputText or something but that is basically the same as the base input thus also does not have access to any errors. I'm also unable to alter the HTML of the elements.input inside the field constructor.
Have a look at play documentation: Writing your own field constructor.
You can check on errors with #if(elements.hasErrors) within the template of your custom field constructor.
<div class="input-with-label text-left #if(elements.hasErrors){field-error}">
...
Edit:
You can pass the error state of your field via the args parameter to your input. From the play docs:
Note: All extra parameters will be added to the generated HTML, except for ones whose name starts with the _ character. Arguments starting with an underscore are reserved for field constructor argument (which we will see later).
You need to cast to the matching type though.
#input(resumeForm("surname"), '_label -> "Surname", 'hasErrors -> resumeForm("surname").hasErrors) { (id, name, value, args) =>
<input name="#name" type="text" value="#value" placeholder="Enter your surname"
class="#if(args.get('hasErrors).map(_ match { case x:Boolean => x}).get){field-error}">
}

Optional checkbox in Scala Play - form does not validate

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

Play framework null checkbox list

I am new to play framework having some dificulties in accesing check box in controller. My view is:-
#(img:Form[Image])
#helper.form(action = routes.Application.abc) {
<li><input name="item[0]" value="pt" type=checkBox></li>
<li><input name="item[1]" value="sumit" type=checkBox></li>
<p>
<button type=submit id=imgButton>submit</button>
</p>
}
My conntroller is:-
def abc = Action{
implicit request =>
val values =ImageForm.bindFromRequest.get
println("mapinggg"+values)
Ok("hi")
}
My case class to handle checkbox is:-
case class Image (desc:List[String])
and form is
val ImageForm =Form(
mapping(
"desc" -> list(text)
)(Image.apply)(Image.unapply)
)
But it returns nill when I click on submit by selecting checkbox?
It gives output as Image(List()) but I want list of selected checkboxes
The names of your inputs ("item" - without the indices) needs to match the key of your list mapping (here given as "desc") for the binding to succeed. It should work if you change the ImageForm mapping to:
val ImageForm =Form(
mapping(
"item" -> list(text)
)(Image.apply)(Image.unapply)
)
Note that the actual field name in your case class - "desc" - should not matter here.

One text input and a submit button in scala

I'm looking for a solution to accomplish the same thing as in this similar question:
HTML forms with java Play Framework 2
But in Scala. Is there a way to do this? I just have one text field and a submit button. I want to get the value from the text field when pressing my button and pass this value to backend code.
object MyController extends Controller {
val submissionForm = Form(
single("myvalue" -> text)
)
def myaction = TODO //as like #mbarlocker told before
}
On such a simple case of a single input, you don't have to use mapping in form definition and you don't have to use any template helper at the input. Template helpers are useful but in this case you get freedom over the layout, without writing a custom field constructor. For example, if you need to place the the submit button next to the input field, just write something like this:
#form(routes.Application.myaction, 'class -> "form-inline") {
<input type="text" id="myvalue" name="myvalue" value="#submissionForm.data.get("myvalue")">
<input type="submit" class="btn" value="Submit :)">
}
Basically, set up the view & form the same way as in HTML forms with java Play Framework 2, and then put this in your controller.
object MyController extends Controller {
case class Submission(value: String)
val submissionForm = Form(
mapping(
"value" -> text
)(Submission.apply)(Submission.unapply)
)
def myaction = Action { implicit request =>
submissionForm.bindFromRequest().fold(
formWithErrors => {
// do something with the bad form, like reshow the view
Ok("got a bad form")
},
submission => {
// do something with the submitted form
Ok("got " + submission.value)
}
)
}
}
Play 2.1 Forms Documentation