0

I have ...

  • a Seq[RoleId] defining all roles that a user can get granted
  • a Userwith a property roles: Seq[RoleId], where the roles are those that the user has got granted
  • a Play controller preparing a form and providing the user (including her roles) and the Seq of available RoleIds to the html page as part of the form's data (including the mapping)
  • a Twirl template showing a checkbox for each available role

What I'd like to achieve is a list of checkboxes where every checkbox who's value is part of the user's roles is checked. So the list should show which of the available roles are granted to the user, as shown in this very refined prototype:

Name:  [Doe, John]
Roles: [ ] Admin
       [x] Manager
       [x] Service Desk
       [ ] Jack of all trades

if the user's name is John Doe with the roles Manager and Service Desk.

That seems rather simple, but I can't find a way to achieve it without some hacks (like circumventing the form and moving the role date to the Twirl template as a regular parameter; writing a custom mapper in the form handling code etc.). Isn't there a way to do it in the Play way without all that boilerplate ?

I googled hard, but I couldn't find any example that seemed to do it right. And Play's form processing docs weren't helpful either.

Any ideas ?

1
  • You need to be more specific about what's wrong, what's the error if you want to get helped. Commented Mar 21, 2019 at 20:23

2 Answers 2

1

Upon refining my question I came up with a solution that worked.

I use the following form, which uses the case classes below to hold its data. As you can see there's a nested mapping inside, representing a list of roles and their state with regard to the user's roles (true meaning that the user has the role, false otherwise):

def userForm(implicit messages: Messages): Form[UserFormData] = Form(
  mapping(
    "firstName" -> nonEmptyText(minLength = 2, maxLength = 30),
    "lastName" -> nonEmptyText(minLength = 2, maxLength = 40),
    "email" -> email,
    "roleAssignments" -> seq(
      mapping(
        "roleIdStr" -> nonEmptyText,
        "isAssigned" -> boolean
      )(RoleAssignment.apply)(RoleAssignment.unapply)
    )
  )(UserFormData.apply)(UserFormData.unapply)
)

case class UserFormData(firstName: String, lastName: String, email: String, roleAssignments: Seq[RoleAssignment])
case class RoleAssignment(roleIdStr: String, isAssigned: Boolean)

The controller just populates the form with data from the db:

def user(id: Long) = Action.async { implicit request =>
  managerService.retrieveByIdWithRoles(id, Some(request.identity), request.remoteAddress).map { case (user, roles) =>
    Ok(views.html.admin.administration.show_user(
      userForm.fill(UserFormData(user.firstName, user.lastName, user.email, roleAssignments(roles)))))
  }
}

private def roleAssignments(roles: Seq[Role]) = {
  val roleIds = roles.map(_.id)
  Roles.all.map { case id =>
    RoleAssignment(id.toString, roleIds.contains(id))
  }
}

And in the view template I repeat over the roleAssignments and use the index provided by repeatWithIndexto access the seq's elements:

@repeatWithIndex(userForm("roleAssignments")) { (roleAssignment, index) =>
  @b4.checkbox(userForm(s"roleAssignments[$index].isAssigned"), '_text -> userForm(s"roleAssignments[$index].roleIdStr").value, '_custom -> true, 'readonly -> true)
}

Thank you @cchantep for pushing me to try harder.

Sign up to request clarification or add additional context in comments.

Comments

1

The Play library provides an inputCheckboxGroup which works the same way as the inputRadioGroup.

In your Play controller:

def userForm(implicit messages: Messages): Form[UserFormData] = Form(
  mapping(
    "firstName" -> nonEmptyText(minLength = 2, maxLength = 30),
    "lastName" -> nonEmptyText(minLength = 2, maxLength = 40),
    "email" -> email,
    "roleAssignmentIds" -> seq(text)
  )(UserFormData.apply)(UserFormData.unapply)
)
val roleAssignmentOptions : List[(String, String)] = List("1" -> "Admin", "2" -> "Manager", "3" -> "Service Desk", "4" -> "Jack of All Trades")

Then in your Play Template:

@helper.inputCheckboxGroup(form("roleAssignmentIds"), roleAssignmentOptions)

This will display a list of checkboxes. The IDs ("1".."4") will be sent back back to the server when the checkboxes are checked. (If you fill the form in advance, you need to fill it in with the IDs - "1".."4" - as well.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.