I have read several times the documentation but I still have problems with my nested form in Play! Scala 2.2 (detailed further).
Here is my form :
<form method="post" action="/admin/test2">
first name : <input type="text" name="firstname"> <br>
info 0 : <input type="text" name="label[0]"> <br>
info 1 : <input type="text" name="label[1]"> <br>
<input type="submit" value="test nested form" />
</form>
And the case classes corresponding :
case class Contact(firstname: String,
informations: Seq[ContactInformation])
case class ContactInformation(label: String)
val contactForm: Form[Contact] = Form(
mapping(
"firstname" -> nonEmptyText,
"informations" -> seq(
mapping(
"label" -> nonEmptyText
)(ContactInformation.apply)(ContactInformation.unapply)
)
)(Contact.apply)(Contact.unapply)
)
def saveContact = Action { implicit request =>
contactForm.bindFromRequest.fold(
formWithErrors => Ok("error"),
contact => {
println(contact)
Ok("Done")
}
)
}
I don't get any errors, but the contact that I get from the form (printed with println(contact)) has an empty informations field, i.e. it looks like this : Contact(h,List())
The error comes probably from the html part since I have followed to the letter the documentation from this page : play! scala forms documentation
but I can’t figure it out.
The field names of the inputs should use the full path in the Form. label appears within the informations Mapping, and informations is the sequence, not the label, so you should use informations[0].label instead of label[0].
Your view should look like this:
first name : <input type="text" name="firstname"> <br>
info 0 : <input type="text" name="informations[0].label"> <br>
info 1 : <input type="text" name="informations[1].label"> <br>
Related
I'm working on a Scala Play 2.7.x (you may checkout the project here play-silhouette-seed googleauth branch) and I have a form defined as:
object TotpSetupForm {
val form = Form(
mapping(
"sharedKey" -> nonEmptyText,
"scratchCodes" -> seq(mapping(
"hasher" -> nonEmptyText,
"password" -> nonEmptyText,
"salt" -> optional(nonEmptyText)
)(PasswordInfo.apply)(PasswordInfo.unapply)),
"scratchCodesPlain" -> optional(seq(nonEmptyText)),
"verificationCode" -> nonEmptyText(minLength = 6, maxLength = 6)
)(Data.apply)(Data.unapply)
)
case class Data(
sharedKey: String,
scratchCodes: Seq[PasswordInfo],
scratchCodesPlain: Option[Seq[String]],
verificationCode: String = "")
}
Where PasswordInfo comes from Play-Silhouette and looks like:
case class PasswordInfo(
hasher: String,
password: String,
salt: Option[String] = None
) extends AuthInfo
In my controller I populate the form and pass it as parameter to my view template as follows. Note that I have debugged it and totpInfo.scratchCodes has 5 values and the form is correctly populated:
val formData = TotpSetupForm.form.fill(TotpSetupForm.Data(totpInfo.sharedKey, totpInfo.scratchCodes, totpInfo.scratchCodesPlain))
Ok(views.html.someView(formData, ...)
I render the view as follows, please note that I did read the Scala Forms Repeated Values documentation note :)
#helper.form(action = controllers.routes.TotpController.submit()) {
#helper.CSRF.formField
#b3.text(totpForm("verificationCode"), '_hiddenLabel -> messages("verificationCode"), 'placeholder -> messages("verificationCode"), 'autocomplete -> "off", 'class -> "form-control input-lg")
#b3.hidden(totpForm("sharedKey"))
#helper.repeatWithIndex(totpForm("scratchCodes"), min = 1) { (scratchCodeField, index) =>
#b3.hidden(scratchCodeField, '_label -> ("scratchCode #" + index))
}
<div class="form-group">
<div>
<button id="submit" type="submit" value="submit" class="btn btn-lg btn-primary btn-block">#messages("verify")</button>
</div>
</div>
}
even though the form's scratchCodes sequence is correctly populated, each of the sequence values render as empty:
<input type="hidden" name="scratchCodes[0]" value="" >
<input type="hidden" name="scratchCodes[1]" value="" >
<input type="hidden" name="scratchCodes[2]" value="" >
<input type="hidden" name="scratchCodes[3]" value="" >
<input type="hidden" name="scratchCodes[4]" value="" >
The number of fields in the sequence is correct though.
I have also tried using the #helper.repeat alternative and even using the #helper.input instead of #b3.hidden just to be sure and the result is always the same ... I get empty valued PasswordInfo tuples rendered.
How can I fix this?
OK found the culprit: repeated + nested values require accessing each attribute separately like this:
#helper.repeat(totpForm("scratchCodes"), min = 1) { scratchCodeField =>
#b3.hidden(scratchCodeField("hasher"))
#b3.hidden(scratchCodeField("password"))
#b3.hidden(scratchCodeField("salt"))
}
then works fine and the post request populates the sequence of PasswordInfo UDTs correctly.
I am trying to build an input helper for Play Framework in Scala.
I would like to have my inputs of type radio and checkbox to be printed differently than the standard text or password input types ?
Here is my custom helper:
#(elements: helper.FieldElements)
<div class="form-group">
<label for="#elements.id" class="col-lg-2 control-label">#elements.label</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="#elements.id" />
</div>
</div>
But this prints an text input instead of checkbox input. Which is strange behaviour.
I've read the documentation, but couldn't find anything about it.
Here is how I import this helper to view:
#implicitFieldConstructor = #{ FieldConstructor(generic.views.html.FormHelpers.twitterBootstrapInputSupraClub.render) }
#checkboxFieldConstructor = #{ FieldConstructor(generic.views.html.FormHelpers.twitterBootstrapInputSupraClubCheckbox.render) }
And this is how I call helpers to build input (generates inputs) in view:
#inputText(
accountRegistrationForm("email"),
'_label -> "email"
)
#inputPassword(
accountRegistrationForm("password"),
'_label -> password
)
#inputRadioGroup(
accountRegistrationForm("regulationAcceptance"),
options = Seq("true"->"Yes"),
'_label -> "I agree with regulation",
'type -> "radio"
)(handler = checkboxFieldConstructor, implicitly[Lang])
I might be missing something here but as far as I can see, you seem to me missing the right input type.
In your helper, try changing <input type="text"... to <input type="radio"...
You can pass the input type dynamically, too. e.g.
#(elements: helper.FieldElements, inputType: String)
<div class="form-group">
<label for="#elements.id" class="col-lg-2 control-label">#elements.label</label>
<div class="col-lg-10">
<input type="#inputType" class="form-control" id="#elements.id" />
</div>
</div>
EDIT:
I have something similar to this for radio buttons:
given that my userDetailForm contains canLogOn:
"company" -> nonEmptyText,
"canLogon" -> boolean,
"canTrade" -> boolean,
I created a function in views: radioSet(fieldName: String)
#radioSet(fieldName: String) = {
<label for="#fieldName">Can Logon</label>
<input name=#fieldName id=#fieldName type="radio" value = "true"
#if(userDetailForm(fieldName).value.getOrElse("false").equals("true"))
{checked}>
}
Then I call it when I need it:
#radioSet("canLogon")
And I get: http://imgur.com/RG60wxV (sorry I cant post images yet)
I need to build update method, but when i test show the error NoSuchElementException: None.get
UserController
object UserController extends Controller {
def update(id:Long) = DBAction { implicit rs =>
var user = simpleUserForm.bindFromRequest.get
user.id = Users.toOption(id)
Users.update(user)
Redirect(routes.UserController.list)
}
val simpleUserForm :Form[User] = Form {
mapping(
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText,
"email" -> email,
"birthDate" -> nonEmptyText,
"phone" -> nonEmptyText,
"username" -> text,
"password" -> nonEmptyText
)(UserForm.fromSimpleForm)(UserForm.toSimpleForm)
}
}
edit.scala.html
#import models.auth.Users
#(title: String, user:models.auth.User)
#main(title){
<form method="post" action="#controllers.auth.routes.UserController.update(Users.toLong(user.id))">
<input type="text" placeholder="First Name" name="firstName" value="#user.firstName"/><br/>
<input type="text" placeholder="Last Name" name="lastName" value="#user.lastName"/><br/>
<input type="email" placeholder="Email" name="email" value="#user.email" /><br/>
<input type="text" placeholder="Phone" name="phone" value="#user.phone" /><br/>
<input type="text" placeholder="Birthdate(dd/MM/yyyy)" name="birthDate" value="#user.birthDate" /><br/>
<input type="text" placeholder="Username" name="username" value="#user.username" /><br/>
<input type="submit" value="Update User" />
</form>
}
routes
POST /user/:id/ controllers.auth.UserController.update(id:Long)
I already done for create, read and delete, but for update i found error in line
var user = simpleUserForm.bindFromRequest.get
the error is NoSuchElementException: None.get
The Play page on Scala Forms is helpful here. That approach would be to send the populated form to the view as a parameter, then on submit use fold, which gives you options to deal with the error case as well as the "happy" case. Something like the following (adapted from the above page):
simpleUserForm.bindFromRequest.fold(
formWithErrors => {
// binding failure, you retrieve the form containing errors:
// in your form, test .hasErrors
BadRequest(views.html.user.edit(formWithErrors))
},
userData => {
/* binding success, you get the value. */
// .. do the update
...
//-- and return to list or home or...
Redirect(routes.Application.home(id))
}
)
If you don't want to use the form, then to back to your actual question, would .getOrElse not work?
did you import
import play.api.libs.concurrent.Execution.Implicits._
I want to use Bootstrap 3 validation states like
<div class="form-group has-success">
<label class="control-label" for="inputSuccess1">Input with success</label>
<input type="text" class="form-control" id="inputSuccess1">
</div>
<div class="form-group has-error">
<label class="control-label" for="inputError1">Input with error</label>
<input type="text" class="form-control" id="inputError1">
</div>
how to check the status success? Before first submiting the form field has no error, but this does not mean that the field is correct.
looking a little deeper views.html.helper.FieldElements and play.api.data.Field, i found solution (using this in helper handler)
#(elements: helper.FieldElements)
#state = {
#if(elements.hasErrors) {
has-error
} else {
#if(elements.field.value.isDefined) {
has-success
}
}
}
field.value is None if play.api.data.Form is empty (without filling or binding).
val registerForm = Form(
tuple(
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText
)
)
Ok(views.html.register(registerForm))
How can we create checkboxes and bind them with our form in play2.0 using scala.
If I have
val placeForm = Form(
mapping(
"id" -> ignored(NotAssigned: Pk[Long]),
"url_key" -> nonEmptyText,
"title" -> optional(text),
"page_id" -> optional(longNumber)
)(models.Place.apply)(models.Place.unapply)
)
and I have created form like this.
#form(routes.Page.save) {
#form(routes.Page.save) {
<fieldset>
#inputText(pageForm("title"), '_label -> "Title")
#inputText(pageForm("template"), '_label -> "Template") <label>Options:</label>
<div class="input">
<label>note <input type="checkbox" name="options[]" value="0">
</label> <label>About US <input type="checkbox" name="options[]"
value="0">
</label> <label>Facebook <input type="checkbox" name="options[]"
value="0">
</label> <label>Twitter <input type="checkbox" name="options[]"
value="0">
</label> <label>Hotmail <input type="checkbox" name="options[]"
value="0">
</label> <label>Something <input type="checkbox" name="options[]"
value="0">
</label>
</div>
</fieldset>
Now I want dont want simple html to create these checkboxes and bind these checkbox values into form
Can anyone help me with this
The structure of your val placeForm must match the form that you render in your template.
For example :
val placeForm = Form(
mapping(
"id" -> ignored(NotAssigned: Pk[Long]),
"title" -> optional(text),
"template" -> optional(text),
"checkbox1" -> text
"checkbox2" -> text
) // ... here construction and deconstruction functions
)
Your template can lookes like :
#form(routes.Page.save) {
<fieldset>
#inputText(pageForm("title"), '_label -> "Title")
#inputText(pageForm("template"), '_label -> "Template")
<label>Options:</label>
<div class="input">
<label> 1 <input type="checkbox" name="checkbox1" value="1"> </label>
<label> 2 <input type="checkbox" name="checkbox2" value="2"> </label>
</div>
</fieldset>
}
Now it's important to understand for wich purposes are these checkboxes. If you want to bind there values to your case class Place and your form structure match completely your case class, you can use apply and unaplly method, if no ... you must utilise your custom functions...
((title, template, checkbox1, checkbox2)=> Place(title, template, checkbox1, checkbox2)) //construct function
((place : Place) => Some((place.title, place.template, place.property_that_correspond_to_checkbox1_value,place.property_that_correspond_to_checkbox2_value)) // deconstruct function
Or you can use tuple instead of mapping for a form construction and then get your values simply as tuple values