Luuk D. Jansen Luuk D. Jansen - 4 years ago 252
Java Question

Saving a list of enums in an EBean and a Form

I have a field 'roles' which contains items of an enum. I am trying to edit this list with a Form, but get:

[InvalidPropertyException: Invalid property 'roles[0]' of bean class [models.Device]: Property referenced in indexed property path 'roles[0]' is neither an array nor a List nor a Map; returned value was [SERVER]]


This is my entity:

@Entity
public class Device extends Model {

@Id
@Constraints.Min(1)
public Long id;

@Constraints.Required
public String name;

@Constraints.Required
public String ipAddress;

@Constraints.Required
@ElementCollection(fetch = FetchType.EAGER)
public Set<DeviceRole> roles = new HashSet<DeviceRole>(Arrays.asList(DeviceRole.OTHER));

@Version
public Timestamp lastUpdate;

public static Finder<Long,Device> find = new Finder<Long,Device>(
Long.class, Device.class
);

public List<ValidationError> validate() {
/*
List<ValidationError> errors = new ArrayList<ValidationError>();
if (User.byEmail(email) != null) {
errors.add(new ValidationError("email", "This e-mail is already registered."));
}
return errors.isEmpty() ? null : errors;
*/
return null;
}
}


The edit and update functions in the controller:

public static Result edit(Long id) {
Device device = Device.find.byId(id);
Form<Device> myForm = Form.form(Device.class);
myForm = myForm.fill(device);

return ok(views.html.Devices.edit.render(myForm, listOfRoles()));
}

public static Result update() {
Form<Device> deviceForm = Form.form(Device.class).bindFromRequest();

if (deviceForm.hasErrors()) {
return badRequest(views.html.Devices.edit.render(deviceForm, listOfRoles()));
}

// Form is OK, has no errors, we can proceed
Device device = deviceForm.get();
device.update(device.id);
return redirect(routes.Devices.index());
}

private static List<String> listOfRoles() {
List<String> list = new ArrayList<String>();
for(DeviceRole role : DeviceRole.values()) {
list.add(role.toString());
}
return list;
}


And the template:

@main("Edit a device") {

@helper.form(action = routes.Devices.update()) {
@helper.inputText(myForm("name"))
@helper.inputText(myForm("ipAddress"))

@helper.select(myForm("roles"), options(deviceRoles), 'multiple->"multiple")

<input type="hidden" name="id" value="@myForm("id").value">
<input type="submit" value="Submit">
}

<a href="@routes.Devices.index()">Cancel</a>
}

Answer Source

Actually probably for doing it like this you would need to save them as a comma separated list of values in String field, to make it clearer I'd suggest to change DeviceRole to the Ebean model and handle it as common ManyToMany relation.

It gives you more flexibility IMHO and also allows to create more dynamic roles in the future,

Just forked your sample and modified it to show how I'd do it myself (some details in readme file):

https://github.com/biesior/temperature-control

P.S.: Take a look at the Ebean's ticket if nothing changed till now just saving list of enums is not supprted yet (I don't know the current state) http://avaje.org/topic-149.html

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download