selectOneMenu具有复杂的对象,是否需要转换器?

时间:2013-02-15 10:27:23

标签: jsf-2 converter selectonemenu

<h:selectOneMenu><p:selectOneMenu>是否需要使用任意用户创建的类作为其值?我的意思是,以下代码应该在没有转换器的情况下工作吗?

<p:selectOneMenu value="#{bean.language}">
    <f:selectItems value="#{bean.allLanguages}" />
</p:selectOneMenu>

@Named(value = "bean")
@ConversationScoped
public class Bean {

    private Language language; // appropriate getter and setter are present

    public List<SelectItem> getAllLanguages() {
        // populates a list of select items with Strings as item labels and Languages as item values
    }

}

我有一个类似的代码,enum作为类型(语言),它完美无缺。但是当我用普通的java类替换类型时,我得到转换错误。

2 个答案:

答案 0 :(得分:1)

这里需要一个转换器,因为JSF默认采用字符串,这就是你编码的方式。 JSF不知道如何将伪实体转换为字符串,反之亦然。

一些注意事项:

1。您的getAsString方法定义了您的实体/ POJO的标识符,而不是JSF(或其他)选择作为itemLabel的标识符。

2。您的转换器可以使用这篇臭名昭着的文章挖掘真实实体的数据库:

http://balusc.blogspot.de/2011/09/communication-in-jsf-20.html#ConvertingAndValidatingGETRequestParameters

您也可以使用带有“模式”的CDI注释。

3。您的value = "bean"是多余的,CDI的选择范围通常是@ViewScoped。但是,您必须记住,如果不使用Seam 3或Apache MyFaces CODI,CDI @Named + JSF @ViewScoped不能协同工作。

答案 1 :(得分:1)

你不需要转换器,如果你使用我写的这个小类:-)它可以返回selectOne和selecteMany组件。它要求您的类的toString()提供对象的一对一唯一表示。如果您愿意,可以替换toString()以外的方法名称,例如toIDString()

要在ManagedBean中使用ListBacker,请在使用ListBacker<Type>

的任何地方使用List<Type>
@ManagedBean
@RequestScoped
public class BackingBean {
    private ListBacker<User> users; // +getter +setter

    @PostConstruct
    public void init() {
        // fill it up from your DAO
        users = new ListBacker<User>(userDAO.find());
    }
    // Here's the payoff!  When you want to use the selected object, 
    // it is just available to you, with no extra database hits:
    User thisOneIsSelected = users.getSelectedItemAsObject();

    // or for multi-select components:
    List<User> theseAreSelected = users.getSelectedItemsAsObjects();
}

在你的xhtml文件中:

<p:selectOneMenu value="#{backingBean.users.selectedItem}">
    <f:selectItems value="#{backingBean.users.contents}" var="item" itemValue="#{item.value}" itemLabel="#{item.label}" />
</p:selectOneMenu>

ListBacker类:​​

public class ListBacker<T extends AbstractEntityBase> {
    // Contains the String representation of an Entity's ID (a.k.a.
    // primary key) and the associated Entity object
    Map<String, T> contents = new LinkedHashMap<String, T>(); // LinkedHashMap defaults to insertion-order iteration.

    // These hold values (IDs), not labels (descriptions).
    String selectedItem; // for SelectOne list
    List<String> selectedItems; // for SelectMany list


    public class ListItem {
        private String value;
        private String label;


        public ListItem(String value, String label) {
            this.value = value;
            this.label = label;
        }

        public String getValue() {
            return value;
        }

        public String getLabel() {
            return label;
        }
    }


    public ListBacker() {}

    public ListBacker(List<T> lst) {
        put(lst);
    }


    public void clear() {
        contents.clear();
        selectedItem = null;
        if(selectedItems != null) {
            selectedItems.clear();
        }
    }

    public List<ListItem> getContents() {
        return convert(contents);
    }

    public String getSelectedItem() {
        return selectedItem;
    }

    public void setSelectedItem(String selectedItem) {
        this.selectedItem = selectedItem;
    }

    public List<String> getSelectedItems() {
        return selectedItems;
    }

    public void setSelectedItems(List<String> selectedItems) {
        this.selectedItems = selectedItems;
    }

    public T getSelectedItemAsObject() {
        return convert(selectedItem);
    }

    public List<T> getSelectedItemsAsObjects() {
        return convert(selectedItems);
    }


    public void put(T newItem) {
        contents.put(newItem.toString(), newItem);
    }

    public void put(List<T> newItems) {
        for (T t : newItems) {
            put(t);
        }
    }


    // PROTECTED (UTILITY) METHODS

    protected List<ListItem> convert(Map<String, T> maps) {
        List<ListItem> lst = new ArrayList<ListItem>();
        for (Entry<String, T> e : maps.entrySet()) {
            lst.add(new ListItem(e.getKey(), e.getValue().desc()));
        }
        return lst;
    }

    protected List<T> convert(List<String> ids) {
        List<T> lst = new ArrayList<T>();
        for (String id : ids) {
            lst.add(convert(id));
        }
        return lst;
    }

    protected T convert(String id) {
        return contents.get(id);
    }

}

我有两个toString()实现,一个用于JPA实体:

public abstract class AbstractEntityBase {
    @Override
    public final String toString() {
        return String.format("%s[id=%s]", getClass().getSimpleName(), getIdForToString().toString());
    }
    /**
    * Return the entity's ID, whether it is a field or an embedded ID class..
    * @return ID Object
    */
    protected abstract Object getIdForToString();
}

和JPA EmbeddedId的一个:

public abstract class CompositeKeyBase {
    @Override
    public final String toString() {
        return String.format("%s[id=%s]", getClass().getSimpleName(), getIdForToString());
    }

    /**
     * Supports the class's toString() method, which is required for ListBacker.
     * Compile a string of all ID fields, with this format:
     * fieldName=StringVALUE,field2=STRINGvAlUE2,...,fieldx=stringvalue <br />
     * Recommended: start with Eclipse's "generate toString()" utility and move it to getIdForToString()
     * @return a 1-to-1 String representation of the composite key
     */
    public abstract String getIdForToString();
}

getIdForToString()的示例实现,用于具有一个Id字段的实体:

@Override
public Object getIdForToString() {
    return userID;
}

getIdForToString()的示例实现,用于具有两个字段的EmbeddedId:

@Override
public String getIdForToString() {
    return "userID=" + userID + ",roleID=" + roleID;
}