How to populate options of h:selectOneMenu from database?

前端 未结 5 2088
情话喂你
情话喂你 2020-11-21 05:03

I am creating a web application, where you have to read a list of objects / entities from a DB and populate it in a JSF . I am unable to

5条回答
  •  佛祖请我去吃肉
    2020-11-21 05:29

    Based on your question history, you're using JSF 2.x. So, here's a JSF 2.x targeted answer. In JSF 1.x you would be forced to wrap item values/labels in ugly SelectItem instances. This is fortunately not needed anymore in JSF 2.x.


    Basic example

    To answer your question directly, just use whose value points to a List property which you preserve from the DB during bean's (post)construction. Here's a basic kickoff example assuming that T actually represents a String.

    
        
    
    

    with

    @ManagedBean
    @RequestScoped
    public class Bean {
    
        private String name;
        private List names; 
    
        @EJB
        private NameService nameService;
    
        @PostConstruct
        public void init() {
            names = nameService.list();
        }
    
        // ... (getters, setters, etc)
    }
    

    Simple as that. Actually, the T's toString() will be used to represent both the dropdown item label and value. So, when you're instead of List using a list of complex objects like List and you haven't overridden the class' toString() method, then you would see com.example.SomeEntity@hashcode as item values. See next section how to solve it properly.

    Also note that the bean for value does not necessarily need to be the same bean as the bean for value. This is useful whenever the values are actually applicationwide constants which you just have to load only once during application's startup. You could then just make it a property of an application scoped bean.

    
        
    
    

    Complex objects as available items

    Whenever T concerns a complex object (a javabean), such as User which has a String property of name, then you could use the var attribute to get hold of the iteration variable which you in turn can use in itemValue and/or itemLabel attribtues (if you omit the itemLabel, then the label becomes the same as the value).

    Example #1:

    
        
    
    

    with

    private String userName;
    private List users;
    
    @EJB
    private UserService userService;
    
    @PostConstruct
    public void init() {
        users = userService.list();
    }
    
    // ... (getters, setters, etc)
    

    Or when it has a Long property id which you would rather like to set as item value:

    Example #2:

    
        
    
    

    with

    private Long userId;
    private List users;
    
    // ... (the same as in previous bean example)
    

    Complex object as selected item

    Whenever you would like to set it to a T property in the bean as well and T represents an User, then you would need to bake a custom Converter which converts between User and an unique string representation (which can be the id property). Do note that the itemValue must represent the complex object itself, exactly the type which needs to be set as selection component's value.

    
        
    
    

    with

    private User user;
    private List users;
    
    // ... (the same as in previous bean example)
    

    and

    @ManagedBean
    @RequestScoped
    public class UserConverter implements Converter {
    
        @EJB
        private UserService userService;
    
        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
            if (submittedValue == null || submittedValue.isEmpty()) {
                return null;
            }
    
            try {
                return userService.find(Long.valueOf(submittedValue));
            } catch (NumberFormatException e) {
                throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e);
            }
        }
    
        @Override
        public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
            if (modelValue == null) {
                return "";
            }
    
            if (modelValue instanceof User) {
                return String.valueOf(((User) modelValue).getId());
            } else {
                throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
            }
        }
    
    }
    

    (please note that the Converter is a bit hacky in order to be able to inject an @EJB in a JSF converter; normally one would have annotated it as @FacesConverter(forClass=User.class), but that unfortunately doesn't allow @EJB injections)

    Don't forget to make sure that the complex object class has equals() and hashCode() properly implemented, otherwise JSF will during render fail to show preselected item(s), and you'll on submit face Validation Error: Value is not valid.

    public class User {
    
        private Long id;
    
        @Override
        public boolean equals(Object other) {
            return (other != null && getClass() == other.getClass() && id != null)
                ? id.equals(((User) other).id)
                : (other == this);
        }
    
        @Override
        public int hashCode() {
            return (id != null) 
                ? (getClass().hashCode() + id.hashCode())
                : super.hashCode();
        }
    
    }
    

    Complex objects with a generic converter

    Head to this answer: Implement converters for entities with Java Generics.


    Complex objects without a custom converter

    The JSF utility library OmniFaces offers a special converter out the box which allows you to use complex objects in without the need to create a custom converter. The SelectItemsConverter will simply do the conversion based on readily available items in .

    
        
    
    

    See also:

    • Our wiki page

提交回复
热议问题