[Play 2.1.3 Scala] Dynamic lists of checkboxes

714 views
Skip to first unread message

Byron Weber Becker

unread,
Aug 24, 2013, 7:58:32 AM8/24/13
to play-fr...@googlegroups.com
I'm having the devil of a time putting together a dynamic list of checkboxes in a view using the form helpers.  I have a workaround, but it feels way to complex;  I'm hoping for a simpler solution.

What's wanted is a form with a list of labeled checkboxes that can be expanded at run-time. Call them tags.

Example:
[ ] Math
[ ] Engineering
[ ] Arts

Add  [ ] Science  to the appropriate database or list structure and it, of course, is added to the view.

It seems natural to define a form using a list of boolean:

  val tagForm = Form(

   mapping(

     "name" -> nonEmptyText,  //unrelated field used to test form validation

     "tags" -> list(boolean)  // a list of checkboxes

   )(TagForm.apply)(TagForm.unapply)

 )


The checkboxes are displayed with:

        @form(action = routes.Application.submit) {

               @inputText(testForm("name"), '_label -> "My name")

               @repeat(testForm("tags")) { tag =>

                   @checkbox(tag)

               }

               <button type="submit">Submit</button>

        }


There are two problems with this approach:
1)  Using list in the form definition leaves no provision for specifying the tag names on the checkboxes.  They are displayed as "tags[0]", "tags[1]", etc.
2)  Browsers only submit values for true checkboxes.  So upon submission, I can't tell which checkboxes were submitted.

My work-around addresses the first issue by including a list of names to decorate the checkboxes as part of the form.  These have to be included in the view as hidden fields so the form is repopulated correctly in the bindFromRequest for error validation.

For the second issue I include a hidden field for each checkbox with a value set to false.  Because it has the same name as the real checkbox, it's returned/found if the checkbox itself is false.  Thus I get a true/false value for each field.

Here's the finished code snippets.  But like I said, it feels like a real hack and I'm sure hoping someone can point me towards something more elegant and maintainable.  Thanks.

Here's the form definition

  val tagForm = Form(

   mapping(

     "name" -> nonEmptyText,            //unrelated field used to test form validation

     "tags" -> list(boolean),           // a list of checkboxes

     "tagNames" -> list(text)           // names for the checkboxes

   )(TagForm.apply)(TagForm.unapply)

 )


And here's the view:

 @form(action = routes.Application.submit) {
 
@inputText(testForm("name"), '_label -> "My name")
     
  @repeat(testForm("tags")) { tag =>
 
    @* Easy access to the corresponding list of tagNames. *@
    @defining(testForm(tag.name.replace("tags", "tagNames"))) { tagName =>
   
      @* This is the checkbox we care about with a label from the list of tagNames *@
      @checkbox(tag, '
_label -> s"${tagName.value.get}")
     
     
@* Browsers only return checkboxes that are true.  This fills in the false values. *@
     
<input type="hidden" name="@tag.name" value="false">
     
     
@* Return the tags so the form processing will fill them in to the completed form. *@
     
<input type="hidden" name="@tagName.name" value="@tagName.value">
   
}
 
}
 
 
<button type="submit">Submit</button>
}



Philip Johnson

unread,
Aug 24, 2013, 1:15:52 PM8/24/13
to play-fr...@googlegroups.com
Note that the situation gets even worse when you want the dynamically generated list of checkboxes to map to entities, not primitives (in your case, booleans).  In this case, the best solution I was able to devise (with the help of the Play community) involves a special backing class for the form:


I would not be surprised if your "hack" turns out to be "best practice", but will be interested to see if others can improve on it.

Philip

Byron Weber Becker

unread,
Aug 26, 2013, 6:40:35 AM8/26/13
to play-fr...@googlegroups.com
Philip -- This is very helpful with respect to creating views in general and as another approach to lists of checkboxes.  Thanks for posting.

A couple of notes and/or questions (I use Scala, so please excuse my rustiness with Java...):
  1. When I tried compiling this I received 10 "illegal start of type" errors that all involved "new ArrayList<>();".  Taking the <> out made the errors go away.
  2. I notice that the forms lose data if there is an error.  I see the comment in controllers.Application.postIndex that says "don't call formData.get, pass null instead."  Why?  
  3. I appreciate the example of writing your own view helpers for checkboxes, etc.  Very helpful! 
  4. I agree with you that the FieldConstructor idiom for controls introduces a lot of complexity.  I think I like the simplicity of your approach better.
Byron

Philip Johnson

unread,
Aug 26, 2013, 12:30:55 PM8/26/13
to play-fr...@googlegroups.com

  1. When I tried compiling this I received 10 "illegal start of type" errors that all involved "new ArrayList<>();".  Taking the <> out made the errors go away.
This is Java 7 syntax, you're probably using Java 6. 
  1. I notice that the forms lose data if there is an error.  I see the comment in controllers.Application.postIndex that says "don't call formData.get, pass null instead."  Why?  
Well, because I was lazy and didn't want to check to see if they were valid.   This part could be improved.

Diogo Ribeiro

unread,
Feb 10, 2014, 10:09:27 AM2/10/14
to play-fr...@googlegroups.com
Thanks a lot for all your work Philip.

I know that the developers are ultra busy, but I find it strange that multiple checkboxes are not supported out from the box...
Reply all
Reply to author
Forward
0 new messages