Create register form with password confirmation and validation messages next to field as shown on image below.
Solution:
There are many solutions to this problem. I chose tuple transformation. It provides me error on
tuple
level, instead of globalErrors
.
First step is to create model object:
User
case class User(login: String, password: String)
then define form in controller for
User
case classval registrationForm: Form[User] = Form( mapping( "login" -> nonEmptyText.verifying("user.registration.userExists", User.findByLogin(_) == None), "passwordWithConfirmation" -> tuple( "password" -> text(minLength = 6, maxLength = 30) .verifying("user.registration.passwordOneDigit", name => { (name + "A").split("\\d").size > 1 }), "confirmation" -> text ).verifying("user.registration.passwordDontMatch", verifyPassword(_)).transform( { case (password, confirmation) => password}, (password: String) => ("", "") ) )(User.apply)(User.unapply) ) def verifyPassword(passwordWithConfirmation: Tuple2[String, String]): Boolean = passwordWithConfirmation match { case (password: String, confirmation: String) => password.equals(confirmation) }
The form contains
Play template for the passwordWithConfirmation
which is tuple
mapping for password and confirmation inputs. For this tuple I added verification rule, checking if both fields are equal. Last thing to do is to convert tuple
into data that will be stored in User
entity. For this purpose transform
function is used. The transformation works both ways:
- from form to object
{ case (password, confirmation) => password}
- from object to form
(password: String) => ("", "")
Form
object may look like this:
@(registrationForm: Form[model.User])(implicit flash: Flash) @import helper.twitterBootstrap._ @import helper._ @main { <h2>@Messages("user.registration.registerAccount")</h2> @helper.form(action=routes.UserCtrl.register) { <fieldset> @inputText(registrationForm("login")) @inputPassword(registrationForm("passwordWithConfirmation.password")) @inputPassword(registrationForm("passwordWithConfirmation.confirmation")) </fieldset> <input class="btn btn-primary" type="submit" value="@Messages("user.registration.register")" /> } }Last thing that I need to do is to show error message for unmatched passwords. This will not work automatically, because there is no
passwordWithConfirmation
element in my form.@if(registrationForm.hasErrors) { @registrationForm.error("passwordWithConfirmation") match { case Some(err:FormError) => {<span class="help-inline">@Messages(err.message)</span>} case _ => {} } }