howells699 howells699 - 1 month ago 6
Scala Question

Scala Play! Form Validation With 2 Radio Buttons

I have created a Scala Play program and would like to incorporate two radio buttons onto the same form.


  • Neither radio buttons are set a value when the page is loaded.

  • The options for both radio buttons are either yes or no.



I would like to know how to validate both radios so that Play accepts the form when:


  • Only the first no button is selected



and


  • When the first yes and either the second yes or no button are selected.



Any other scenario I would like an error to be shown using the bindFromRequest.fold method.

Here is my model:

package viewmodels

case class YesNoRadioViewModel2(firstRadio: String, secondRadio:String) {

}

/**
* View model for pages with yes/no style radio questions.
*/
object YesNoRadioViewModel2 {
def apply(form: play.api.data.Form[YesNoRadioViewModel2]) = {
new YesNoRadioViewModel2(
form.data.get("firstRadio").get,
form.data.getOrElse("secondRadio","no"))
}
}


Here is my form:

val yesNoRadioForm2 = Form(
mapping(
"firstRadio" -> text.verifying(!_.isEmpty),
"secondRadio" -> text.verifying()
)
(YesNoRadioViewModel2.apply)(YesNoRadioViewModel2.unapply))


Here is my controller:

def twoRadioPost: Action[AnyContent] = MyCustomAction.async { implicit request =>

yesNoRadioForm2.bindFromRequest.fold(formWithErrors =>
Future(BadRequest(views.html.myproject.twoRadios(formWithErrors)))
,
model =>
Do something
)
}


Any help would be appreciated!

Thanks in advance!

Answer Source

I use Play 2.6.3

Here goes the index.scala.html:

@import models.MyForm.FormData
@(theForm:Form[FormData])(implicit messages: Messages, request:RequestHeader)

@main("Welcome to Play") {
  <h1>Welcome to Play!</h1>
  @if(theForm.hasGlobalErrors) {
    <ul>
    @for(error <- theForm.globalErrors) {
      <li>@error.format</li>
    }
    </ul>
  }
  @helper.form(action = helper.CSRF(routes.HomeController.processForm())){
    @helper.inputRadioGroup(theForm("field1"), Seq("Yes" -> "Yes", "No" -> "No"))
    @helper.inputRadioGroup(theForm("field2"), Seq("Yes" -> "Yes", "No" -> "No"))
    <button type="submit">Send</button>
  }
}

here goes MyForm object defined in models package:

package models

import play.api.data.Form
import play.api.data.Forms._

/**
  * Created by alex on 8/17/17.
  */
object MyForm {
  case class FormData(firstYesNo:Option[String], secondYesNo:Option[String])

  val theForm = Form(
    mapping(
      "field1" -> optional(text),
      "field2" -> optional(text)
    )(FormData.apply)(FormData.unapply) verifying(
        "Form failed the validation",
        fields => fields match{
          case formData => formData.firstYesNo match{
            case None => false
            case Some("No") => if(!formData.secondYesNo.isDefined) true else false
            case Some("Yes") => if(formData.secondYesNo.isDefined) true else false
          }
        }
      )
  )
}

Here is the code of my only controller:

import javax.inject._
import models.MyForm.theForm
import play.api.mvc._

/**
 * This controller creates an `Action` to handle HTTP requests to the
 * application's home page.
 */
@Singleton
class HomeController @Inject()(cc: ControllerComponents) extends AbstractController(cc) with play.api.i18n.I18nSupport{

  /**
   * Create an Action to render an HTML page.
   *
   * The configuration in the `routes` file means that this method
   * will be called when the application receives a `GET` request with
   * a path of `/`.
   */
  def index() = Action { implicit request: Request[AnyContent] =>
    Ok(views.html.index(theForm))
  }

  def processForm() = Action{implicit request:Request[AnyContent] =>
    theForm.bindFromRequest().fold(
      formWithErrors => BadRequest(views.html.index(formWithErrors)),
      data => Ok("Form successfully submitted")
    )
  }
}

Content of my routes file:

GET     /                           controllers.HomeController.index

POST    /processForm                controllers.HomeController.processForm

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.versioned(path="/public", file: Asset)

And finally, we need to add this to the application.conf:

play.filters.enabled += play.filters.csrf.CSRFFilter

Hope this helps you.