Hegemon
Hegemon

Reputation: 433

How to bind and process a list of checkboxes in a form using the play framework 2

I am using the Play framework 2.2. I have a question concerning multiple checkboxes in a form and how to correctly bind plus process them. I am looking for a Java solution.

Let's assume we have a blogging site. We want to implement the functionality that when users create/edit a blog entry, they have the option of adding zero to many tags to that blog entry. These tag options appear as a long list of checkboxes on the form. For instance, if he/she creates the blog entry "10 things to do on the moon in your spare time" and wants to associate it with the "star gazing", "walking", and "golf" tags, he/she would simply check the appropriate checkboxes in the creation process.

I am unsure of how to bind the models to the view or how to process the list of checkboxes once the request has been submitted. Here is an experimental code sample.

First we have the models: (They are ebean entities)

public class Blog extends Model {
  ...
  @ManyToMany(cascade=CascadeType.REMOVE)
  public List<Tag> tags = new ArrayList<>();
  ...
}

public class Tag extends Model {
  @Id
  public Long id;
  public String name;
  ...
}

In the view, we pass the full list of tags to the view: (I am not sure if this method is correct)

@(tags: java.util.List[Tag], blogEntryForm: Form[Blog])
...
@form(routes.Application.create(), 'id -> "blogEntryForm") {
  ...
  @for((tag, index) <- tags.zipWithIndex) {
    <div class="checkbox">
      <label>
        <input type="checkbox" name="tags[@index]" value="@tag.id">
        @tag.name
      </label>
    </div>
  }
  ...
}

In the controller, we process the form:

public static Result createBlogEntry() {
  Form<Blog> filledForm = Form.form(Blog.class).bindFromRequest();
  if (filledForm.hasErrors()) {
    ...        
  }
  // process checkboxes
  ???
} 

Any help would be much appreciated.

Upvotes: 1

Views: 1707

Answers (1)

mgeiger
mgeiger

Reputation: 77

This seems to work.

@(tags: java.util.List[Tag], blogEntryForm: Form[Blog])

@helper.form(routes.Application.createBlogEntry(), 'id -> "blogEntryForm") {

  @for((tag, index) <- tags.zipWithIndex) {
    <div class="checkbox">
      <label>
        <input type="checkbox" name="tags[@index].id" value="@tag.id">
        @tag.name
      </label>
    </div>
  }
  <input type="submit" />

}

in my controller I have

public static Result renderTestForm() {
    Tag tag1 = new Tag();
    tag1.id = 1L;
    tag1.name = "tag1";
    Tag tag2 = new Tag();
    tag2.id = 2L;
    tag2.name = "tag2";
    List<Tag> tagList = new ArrayList<>();
    tagList.add(tag1);
    tagList.add(tag2);
    Form<Blog> blogForm = Form.form(Blog.class);
    return ok(views.html.testForm.render(tagList, blogForm));
}

public static Result createBlogEntry() {
    Form<Blog> filledForm = Form.form(Blog.class).bindFromRequest();
    if (filledForm.hasErrors()) {
        return ok();
    }
    // process checkboxes
    Blog b = filledForm.get();
    b.save();
    play.Logger.debug(filledForm.toString());
    play.Logger.debug(filledForm.get().toString());
    List<Blog> bList = Blog.finder.all();
    List<Tag> tList = Tag.finder.all();
    play.Logger.debug("********");
    play.Logger.debug(bList.toString());
    play.Logger.debug(tList.toString());
    play.Logger.debug("********");
    return ok();
} 

This is just to test that the whole thing works, so I create two tags for the checkboxes in the view and render them. The createBlogEntry method just produces debugging output to make sure that the data was bound to the Blog objec successfully.

My routes file has these.

GET     /testForm                   controllers.Application.renderTestForm
POST    /testForm                   controllers.Application.createBlogEntry

and here are my models.

@Entity
@Table(name = "blogs")
public class Blog extends Model {

    @Id
    public Long id;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "blog_tags")
    public List<Tag> tags = new ArrayList<>();

    public String toString() {
        String s = "";
        for (Tag t : tags) {
            s += " " + t.toString();
        }
        return s;
    }

    public static final Finder<Long, Blog> finder = new Finder<>(Long.class, Blog.class);

}

@Entity
@Table(name = "tags")
public class Tag {

    @Id
    public Long id;
    public String name;

    @ManyToMany(mappedBy = "tags")
    public List<Blog> blogs = new ArrayList<>();

    public static final Model.Finder<Long, Tag> finder = new Model.Finder<>(Long.class, Tag.class);

}

I didn't have annotations in my code because I didn't use a database while I was testing whether it would work.

Upvotes: 2

Related Questions