[原创于:http://happydev.iteye.com]
实现对象选择可以直接通过下拉选择框的方式来选择,但这种选择不适合数据量较大的对象选择。
而在企业的Web开发中,经常会用到对关联对象的单选或多选,而关联对象的数据量往往不是下拉选择框可以展现的。
本文提出了一个较为通用的关联对象选择方案。
一、一个较为通用的关联对象选择方案要解决的主要问题
- 可能是单选,也可能会是多选;
- 将对象进行“可选择对象”的封装,以支持是否选中状态的维护;
- 待选对象的数据查询;
- 选择时表现层界面viewId的配置;
- 选择后返回的viewId的配置;
二、基础框架设计
/**
* 可选择对象封装模板
* @author happydev
*
* @param <T>
*/
public class SelectableItem <T>{
public SelectableItem(boolean selected, Object id, String itemInfo, T item){
this.selected = selected;
this.id = id;
this.itemInfo = itemInfo;
this.item = item;
}
/**
* 是否已被选择
*/
private boolean selected = false;
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
private Object id;
private String itemInfo;
private T item;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
public String getItemInfo() {
return itemInfo;
}
public void setItemInfo(String itemInfo) {
this.itemInfo = itemInfo;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
/**
* 对象选择基础封装模板类
* @author happydev
*
* @param <T>
*/
public abstract class BaseSelector<T> {
@In
EntityManager entityManager;
private String ret;
protected String selIds;
private boolean submited = false;
private boolean selectOne = false;
/**
* 选择复选框ajax事件,处理选中或取消操作
* @param item
*/
public void onChange(SelectableItem<T> item){
if (selectOne && item.isSelected()){
//如果是单选,则要将已选中的其它项取消
for (SelectableItem<T> t : selectableItems){
if (t.isSelected() && t != item){
t.setSelected(false);
}
}
}
}
/**
* 开始单个对象选择
* @param ret,选择结束后需要返回的viewId
* @param selIds,已被的对象id字符串,用','分隔
* @return,返回选择页面的viewId
*/
public String startSelOne(String ret, String selIds){
selectOne = true;
return startSel(ret, selIds);
}
/**
* 开始多个对象选择
* @param ret,选择结束后需要返回的viewId
* @param selIds,已被的对象id字符串,用','分隔
* @return,返回选择页面的viewId
*/
public String startSelMany(String ret, String selIds){
selectOne = false;
return startSel(ret, selIds);
}
public String startSel(String ret, String selIds){
this.ret = ret;
this.selIds = selIds;
submited = false;
return getViewId();
}
/**
* 抽象方法,返回选择页面的viewId
* @return
*/
protected abstract String getViewId();
/**
* 选择结束
* @return,选择结束后需要返回的viewId
*/
public String ok(){
if (ret == null || ret.length() < 1){
return "/welcome/welcome.xhtml";
}
submited = true;
return ret;
}
/**
* 选择取消
* @return,选择结束后需要返回的viewId
*/
public String cancel(){
if (ret == null || ret.length() < 1){
return "/welcome/welcome.xhtml";
}
selectableItems = null;//清空
return ret;
}
/**
* 抽象方法,取得对象的标签(名称)
* @param obj
* @return
*/
protected abstract String getObjectName(T obj);
/**
* 抽象方法,取得对象的Id
* @param obj
* @return
*/
protected abstract Object getObjectId(T obj);
/**
* 取得已被选择的Id字符串,用','分隔
*/
public String getSelectedStr(){
List<T> l = getSelectedList();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < l.size() ; i++){
sb.append(getObjectName(l.get(i)));
if ( i != l.size() -1){
sb.append(",");
}
}
return sb.toString();
}
/**
* 取得已被选择的对象列表
* @return
*/
public List<T> getSelectedList(){
List<T> l = new ArrayList<T>();
for (SelectableItem<T> item : getSelectableItems()){
if (item.isSelected()){
l.add(item.getItem());
}
}
return l;
}
private List<SelectableItem<T>> selectableItems = null;
private void init(){
List<Long> l_selIds =new ArrayList<Long>();
if (selIds != null && selIds.length() > 0){
String[] arr = selIds.split(",");
if (arr != null){
for (String id : arr){
l_selIds.add(Long.parseLong(id));
}
}
}
List<T> list = getAllObjectToSelect();
for (T p : list){
selectableItems.add(new SelectableItem<T>(l_selIds.contains(getObjectId(p)), getObjectId(p),getObjectName(p),p));
}
}
/**
* 抽象方法,取得所有可被选择的对象列表
* @return
*/
protected abstract List<T> getAllObjectToSelect();
public List<SelectableItem<T>> getSelectableItems() {
if (selectableItems == null){
selectableItems = new ArrayList<SelectableItem<T>>();
init();
}
return selectableItems;
}
public boolean isSubmited() {
return submited;
}
public void setSubmited(boolean submited) {
this.submited = submited;
}
public boolean isSelectOne() {
return selectOne;
}
}
三、具体的实现示例
角色选择器具体的实现代码:
@Name("roleSelector")
@Scope(value = ScopeType.CONVERSATION)
public class RoleSelector extends BaseSelector<Role>{
@Override
protected List<Role> getAllObjectToSelect() {
return entityManager.createQuery("select o from Role o").getResultList();
}
@Override
protected Object getObjectId(Role obj) {
return obj.getId();
}
@Override
protected String getObjectName(Role obj) {
return obj.getRoleName();
}
@Override
protected String getViewId() {
return "/f_sel/role_sel.xhtml";
}
}
角色选择页面代码:
<s:div styleClass="message" id="selectedStr"> <h:outputText value="当前选中(单选):#{roleSelector.selectedStr}" rendered="#{roleSelector.selectOne}" /> <h:outputText value="当前选中(多选):#{roleSelector.selectedStr}" rendered="#{not roleSelector.selectOne}" /> </s:div> <h:form id="selectForm"> <rich:datascroller align="left" for="peopleList" maxPages="20" rendered="#{roleSelector.selectableItems.size>20}"/> <rich:spacer height="30" rendered="#{roleSelector.selectableItems.size>20}"/> <rich:dataTable width="100%" id="peopleList" rows="20" columnClasses="col" value="#{roleSelector.selectableItems}" var="item"> <f:facet name="header"> <rich:columnGroup> <h:column> <h:outputText styleClass="headerText" value="选择" /> </h:column> <h:column> <h:outputText styleClass="headerText" value="角色名称" /> </h:column> </rich:columnGroup> </f:facet> <h:column> <h:selectBooleanCheckbox value="#{item.selected}"> <a4j:support event="onclick" reRender="selectedStr,selectForm" action="#{roleSelector.onChange(item)}"/> </h:selectBooleanCheckbox> </h:column> <h:column> <h:outputText value="#{item.item.roleName}" /> </h:column> </rich:dataTable> <div class="actionButtons"> <h:commandButton value="确定" action="#{roleSelector.ok}" styleClass="buttonStyle"> <s:conversationId/> </h:commandButton> <h:outputText value="  " /> <s:button value="取消" action="#{roleSelector.cancel}" styleClass="buttonStyle"/> </div> </h:form>
用户编辑界面中分配角色的链接代码:
<s:decorate template="/layout/display.xhtml"> <ui:define name="label">分配角色</ui:define> <h:outputText value="#{userHome.instance.getAllRoleStr()}" rendered="#{not roleSelector.submited}" /> <h:outputText value="#{roleSelector.selectedStr}" rendered="#{roleSelector.submited}" /> <h:outputText value=" | " /> <h:commandLink action="#{roleSelector.startSelMany('/f_sys/user.xhtml', userHome.instance.getAllRoleIds())}" view="/f_sel/role_sel.xhtml" value="选择"> <s:conversationId/> </h:commandLink> </s:decorate>
在用户对象保存时对所选择角色的保存:
if (roleSelector.isSubmited()) {
List<Role> l = roleSelector.getSelectedList();
instance.setRoles(l);
}