Scala Play – adding a Login Form
Following on from the previous post about Scala Play, here’s how to add a login form.
This is based upon the Scala Play tutorial.
We’ll need a data object – save the following code in app/controllers/Auth.scala.
package controllers case class UserData(email: String, password: String) |
case class UserData(email: String, password: String)
This is an object which represents the data behind the login form. We’ll also need a Form which maps to this object (I’ve embedded it into an Auth object) – so our app/controllers/Auth.scala file looks like:-
package controllers import play.api.data.Form import play.api.data.Forms._ case class UserData(email: String, password: String) object Auth { val userDataForm = Form( mapping( "email" -> nonEmptyText, "password" -> nonEmptyText )(UserData.apply)(UserData.unapply) ) } |
import play.api.data.Form
import play.api.data.Forms._
case class UserData(email: String, password: String)
object Auth {
val userDataForm = Form(
mapping(
"email" -> nonEmptyText,
"password" -> nonEmptyText
)(UserData.apply)(UserData.unapply)
)
}
We need a view which shows this form (save the following as app/views/login.scala.html):-
@(userform: Form[UserData]) |
- @for(error error.message
- }
}
@helper.form(action = routes.Auth.doLogin()) {
@helper.inputText(userform(“email”))
@helper.inputPassword(userform(“password”))
}Main Site
Add a controller action to app/controllers/Auth.scala display the form and handle form submission:-
package controllers import controllers.Application._ import play.api.data.Form import play.api.data.Forms._ import play.api.mvc.Action case class UserData(email: String, password: String) object Auth { val userDataForm = Form( mapping( "email" -> nonEmptyText, "password" -> nonEmptyText )(UserData.apply)(UserData.unapply) ) def login = Action { Ok(views.html.login(userDataForm)) } def doLogin = Action { implicit request => userDataForm.bindFromRequest.fold( formWithErrors => { BadRequest(views.html.login(formWithErrors)) }, userData => { Redirect(routes.Application.index).withSession(request.session + ("loginStatus" -> "loggedIn") + ("user" -> userData.email)) } ) } } |
import controllers.Application._
import play.api.data.Form
import play.api.data.Forms._
import play.api.mvc.Action
case class UserData(email: String, password: String)
object Auth {
val userDataForm = Form(
mapping(
"email" -> nonEmptyText,
"password" -> nonEmptyText
)(UserData.apply)(UserData.unapply)
)
def login = Action {
Ok(views.html.login(userDataForm))
}
def doLogin = Action { implicit request =>
userDataForm.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.login(formWithErrors))
},
userData => {
Redirect(routes.Application.index).withSession(request.session +
("loginStatus" -> "loggedIn") + ("user" -> userData.email))
}
)
}
}
Add a route to conf/routes to display the form and handle form submission:-
GET /login controllers.Auth.login POST /doLogin controllers.Auth.doLogin |
After running it up with ./sbt ~run, visit http://localhost:9000/login to see it in action (not particularly pretty).
Let’s make it prettier, using the Bootstrap framework. Add the WebJar and Bootstrap webjar into the build.sbt file near the bottom.
libraryDependencies ++= Seq( "org.webjars" %% "webjars-play" % "2.3.0-2", "org.webjars" % "bootstrap" % "3.3.2-2" ) |
You’ll need to restart SBT if you’re using it in reloadable mode.
We need some Field Helpers to make the form inputs match the Bootstrap inputs, so create a views/helpers/loginfieldconstructor.scala.html file which provides the HTML representation:-
@(elements: helper.FieldElements)
@elements.input
Create the code supporting the HTML in app/controllers/LoginFieldHelpers.scala:-
package controllers object LoginFieldHelpers { import views.html.helper.FieldConstructor implicit val myFields = FieldConstructor(views.html.helpers.loginfieldconstructor.f) } |
object LoginFieldHelpers {
import views.html.helper.FieldConstructor
implicit val myFields = FieldConstructor(views.html.helpers.loginfieldconstructor.f)
}
Add a route to serve the static assets by editing the conf/routes file
GET /assets/*file controllers.Assets.at(path="/public", file)
|
Create the stylesheet at public/stylesheets/signin.css:-
body { padding-top: 40px; padding-bottom: 40px; background-color: #eee; } .form-signin { max-width: 330px; padding: 15px; margin: 0 auto; } .form-signin .form-signin-heading, .form-signin .checkbox { margin-bottom: 10px; } .form-signin .checkbox { font-weight: normal; } .form-signin .form-control { position: relative; height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 10px; font-size: 16px; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="email"] { margin-bottom: -1px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; } |
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
Change the login.scala.html file to the following:-
@import LoginFieldHelpers._ @(userform: Form[UserData])<script src="&routes.Assets.at(" type="text/javascript"></script> |
@(userform: Form[UserData])<script src="&routes.Assets.at(" type="text/javascript"></script>
@if(userform.hasGlobalErrors) {
- @for(error error.message }
} @helper.form(action = routes.Auth.doLogin, ‘_submit -> “Login”, ‘class -> “form-signin”) {
Please sign in
@helper.input(userform(“email”), ‘_label -> “Email”, ‘class -> “form-control”, ‘placeholder -> “Email Address”, ‘required -> None, ‘autofocus -> None) { (id, name, value, args) => } @helper.inputPassword(userform(“password”), ‘_label -> “Password”, ‘class -> “form-control”, ‘placeholder -> “Password”, ‘required -> None) }Main Site
Start SBT again (./sbt ~run) if you’ve not done so, and see that the form has now changed to a nicer layout.
Code available in Git under tag 2.0, and demo running in Heroku.