Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding enum to form <select> element in Play! Framework 2.1

I'm trying to figure out best practice to bind enum to form drop-down <select> in Play! 2.0

Here is my enum:

public enum ContactType {
    CLIENT(1),
    CONTRACTOR(2),
    SUPPLIER(3);

    public final int id;

    ContactType(int id) {
      this.id = id;
    }
}

And here's what i'd like to get as result in my view:

<select name="contactType">
  <option value="1">CLIENT</option>
  <option value="2">CONTRACTOR</option>
  <option value="3">SUPPLIER</option>
</select>
like image 801
ainla Avatar asked Jul 31 '13 18:07

ainla


3 Answers

Assuming you are putting the select into a HTML form, the way it is done in our shop is we add a Map to the Java enum and then use the select form helper provided by the framework:

enum:

public enum ContactType {
    CLIENT(1),
    CONTRACTOR(2),
    SUPPLIER(3);

    public final int id;

    ContactType(int id) {
      this.id = id;
    }

    public static Map<String, String> options(){
        LinkedHashMap<String, String> vals = new LinkedHashMap<String, String>();
        for (ContactTypecType cType: ContactType.values()) {
            vals.put(cType.id(), cType.name());
        }
        return vals;
    }
}

view:

@(contactForm: Form[models.Contact])

import helper._

<html><head></head><body>
@helper.form(routes.Contact.formHandlerMethod()){
    @* 
     * Here contactForm("contactType") assumes that you want to map the 
     * ContactType selected to a field in a Contact class (which we've 
     * wrapped in play.data.Form<T> so that the fields in Contact become 
     * inputs in our form
     *@
    @select(contactForm("contactType"), options(ContactType.options())
}
</body></html>

Using this design pattern you can leverage the helper functions in the play.data.Form class to validate and bind your form data in your Contact Controller and you benefit from having less logic in your view, and putting the logic in a place where it can easily be re-used throughout your app.

If, on the other hand, you are using the select on it's own and driving actions in some other way, then Aerus' answer is probably the most straightforward way.

like image 71
2manyprojects Avatar answered Oct 19 '22 18:10

2manyprojects


Something like this in your template should work:

<select name="contactType">
    @for(cType <- ContactType.values()){
        <option value="@cType.id">@cType.name()</option>
    }
</select>

Note: it may be better to use toString() instead of name(). If you override toString() in your enum you could return Contractor instead of CONTRACTOR.

Note 2: if your enum is not in the models package you need to prefix it with the right package name i.e.: @for(cType <- com.my_company.enums.ContactType)

like image 22
Aerus Avatar answered Oct 19 '22 18:10

Aerus


EDIT: The form should not include all the classpath to the enum, just the enum name. So on the ContactType class, the put method has to be replaced to this:

            put(value.name(),name);

I'm not able to bind the enum to the class. In the controller I have something like that:

public Result saveUser() {
    Form<User> userForm = form(User.class).bindFromRequest();
    if(userForm.hasErrors()) {
        return badRequest(createUser.render(userForm));
    }
    User user=userForm.get();
    user.save();
    Logger.info(user.contacts.toString());
    flash("success", "User " + userForm.get().identity + " has been created");
    return GO_ADMIN;
}

Which is returning me all the time "error.invalid". The POST response looks like that:

identity=asas&
status=1&
contacts[0].name=fasddf&
contacts[0].prename=afadfs&
contacts[0][email protected]&
contacts[0].contactType=models.constats.ContactType.MANAGER

Just to have more information, I provide here the User and the Contact classes

User

public class User extends Model{
    @Constraints.Required(message = "Field is required")
    public String identity;
    public Integer status;
    @Valid
    public List<Contact> contacts; 
}

Contact

public class Contact extends Generic{

    @Constraints.Required(message = "Field is required")
    public String name;

    @Constraints.Required(message = "Field is required")
    public String prename;

    @Constraints.Required(message = "Field is required")
    @Constraints.Email
    public String email;

    public ContactType contactType; 
}

ContactType

public enum ContactType {

    ADMIN,SUPPORT,MANAGER,DEVELOPER,DESIGNER,NO_INFORMATION;
}
    public static final Map<String,Object> types=new HashMap<String,Object>()
    {{
        for (ContactType value : ContactType.values()) {
            String name=value.name().toLowerCase();
            name=Character.toUpperCase(name.charAt(0)) + name.substring(1).replaceAll("_", " ");
            put(ContactType.class.getName() + "."+ value.name(),name);
        }
    }};
like image 21
Didac Montero Avatar answered Oct 19 '22 17:10

Didac Montero