1

I want to dynamically create a dropdown (HtmlSelectOneMenu) with all possible options. The option currently set should be preselected. To achieve this I created a value expression for my form:

String jsfValue = String.format("#{%s.item.%s}", getControllerBeanName(), key);
ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, String.class);
menu.setValueExpression("value", valueExpression);

The string #{%s.item.%s} evaluates to #{playlistController.item.category} and category is the object of type Category that I want to bind to my dropdown.

To use the SelectItemsConverter from OmniFaces I changed the toString() method of Category to:

@Override
public String toString() {
  return String.format("%s[id=%d]", getClass().getSimpleName(), getId());
}

My code for the form generation looks like this (Note: Category extends BaseEntity):

  private UIComponent createDropdown(FormInput property) {
    String key = property.getKey();
    String beanName = key + "Controller";
    GenFormBaseController controller = (GenFormBaseController) JSFUtils.getManagedBean(beanName);

    List<BaseEntity> list = controller.getService().findAll();
    List<SelectItem> selectItems = new ArrayList<>(list.size());
    for (BaseEntity itemInList : list) {
      selectItems.add(new SelectItem(itemInList, itemInList.getName()));
    }

    UISelectItems items = new UISelectItems();
    items.setValue(selectItems.toArray());

    HtmlSelectOneMenu menu = new HtmlSelectOneMenu();
    menu.setConverter(new SelectItemsConverter());
    menu.setId(key);
    menu.getChildren().add(items);

    String jsfValue = String.format("#{%s.item.%s}", getControllerBeanName(), key);
    ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, String.class);
    menu.setValueExpression("value", valueExpression);

    return menu;
  }

If I set menu.setConverter(new SelectItemsConverter()); then the wrong item is preselected. But If I remove it, then the correct item is selected but when I try to save the form it fails because it has no converter for the dropdown.

Can anyone help me? I've published the code on GitHub. The method createDropdown can be found at the bottom of the linked code.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Benny Code
  • 51,456
  • 28
  • 233
  • 198

2 Answers2

2

Your mistake is here, in the String.class type argument:

ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, String.class);

A Category is not a String. Use Category.class instead and everything should be well. At least, theoretically. I can't copy'n'paste'n'run this piece of code without changes.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hi BalusC. Thank you for your answer. I've tried it and it did not work. But I made the following observations: When using "Category.class" in the value expression, then every item in the select box will be selected ( – Benny Code Apr 10 '14 at 09:15
  • You should have receivced a `Validation Error: Value is not valid` on submit then, but you didn't make a mention of it. See also http://stackoverflow.com/a/9069660 and http://stackoverflow.com/a/17343582 – BalusC Apr 10 '14 at 10:22
1

I found the error in the code. Simply the equals() and hashCode() of the BaseEntity were insufficient. And the error BalusC already mentioned. The resulting code looks like the following:

GenFormBaseController.java

private UIComponent createDropdown(FormInput property) {
  String key = property.getKey();
  Class<?> expectedType = property.getValue();

  String beanName = lowerFirstChar(expectedType.getSimpleName()) + "Controller";
  GenFormBaseController controller = (GenFormBaseController) JSFUtils.getManagedBean(beanName);

  List<BaseEntity> list = controller.getService().findAll();
  List<SelectItem> selectItems = new ArrayList<>(list.size());
  for (BaseEntity itemInList : list) {
    selectItems.add(new SelectItem(itemInList, itemInList.getName()));
  }

  UISelectItems items = new UISelectItems();
  items.setValue(selectItems);

  HtmlSelectOneMenu menu = new HtmlSelectOneMenu();
  menu.setConverter(new SelectItemsConverter());
  menu.setId(key);
  menu.getChildren().add(items);

  String jsfValue = String.format("#{%s.item.%s}", getControllerBeanName(), key);
  ValueExpression valueExpression = JSFUtils.createValueExpression(jsfValue, expectedType);
  menu.setValueExpression("value", valueExpression);

  return menu;
}

BaseEntity.java

@Override
public int hashCode() {
  int hash = 7;
  hash = 97 * hash + Objects.hashCode(this.id);
  return hash;
}

@Override
public boolean equals(Object obj) {
  if (obj == null) {
    return false;
  }
  if (getClass() != obj.getClass()) {
    return false;
  }
  final BaseEntity other = (BaseEntity) obj;
  if (!Objects.equals(this.id, other.id)) {
    return false;
  }
  return true;
}
Yser
  • 2,086
  • 22
  • 28