分享下拉框无限级联Ajax通用版
闲的无聊没事,就随便写写东西,一直想写个ajax版无限级联下拉框组件,今天就静下心来写咯个,花费我尽4个小时,哦买噶的,为什么我在电脑面前这么有耐力?不扯了,进入正题。
首先我先来几张效果图吧:
页面初次加载时,Aajx方式加载第一个下拉框的数据:

第一个下拉框选项改变时,根据第一个下拉框的value值作为过滤条件去加载第二个下拉框所需数据:

第二个下拉框选项改变时获取第三个下拉框的数据,当然你可以就这样无限的级联下去,我这个组件完全支持:

下面我按大致的实现步骤贴代码并进行一些解释说明:
首先,新建个web项目,名字你随便取,由于我采用的是SSH经典框架组合,所有第一步还是需要导入所需的jar,具体请参看我导入的jar截图:

然后就是web.xml配置,这个一般是几乎很少变动的,顶多是不同项目包路径的变更,其他几乎是固定格式的写法:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="...
<!-- spring注册 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--
spring配置文件放在WEB-INF目录下
<param-value>/WEB_INF/app*.xml</param-value>
-->
<!-- spring配置文件放在src自定义目录下 -->
<param-value>
classpath:/com/yida/config/spring/app*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- struts2注册 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.do</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts-cleanup</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ActionContextCleanUp
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts-cleanup</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 设置请求参数的字符编码 -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encode</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>
freemarker.ext.servlet.FreemarkerServlet
</servlet-class>
<init-param>
<param-name>NoCache</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>ContentType</param-name>
<param-value>text/html</param-value>
</init-param>
<!-- FreeMarker settings: -->
<init-param>
<param-name>default_encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>locale</param-name>
<param-value>zh_CN</param-value>
</init-param>
<init-param>
<param-name>number_format</param-name>
<param-value>0.##########</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>
org.apache.struts2.views.JspSupportServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>StudentServletTest</servlet-name>
<servlet-class>
com.yida.servlet.StudentServletTest
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StudentServletTest</servlet-name>
<url-pattern>/StudentServletTest</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>StudentServletTest2</servlet-name>
<servlet-class>
com.yida.servlet.StudentServletTest2
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StudentServletTest2</servlet-name>
<url-pattern>/StudentServletTest2</url-pattern>
</servlet-mapping>
<jsp-config>
<taglib>
<taglib-uri>/FreeMaker</taglib-uri>
<taglib-location>/WEB-INF/freemaker.tld</taglib-location>
</taglib>
</jsp-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
然后就是搭建项目包的组织结构:如图



由于我懒得再重复搭建项目环境,所以我是在我上篇文章FreeMaker+Ajax分页组件那个测试demo的基础上完成的,所以我把一些跟这次主题有关的文件用红色方框标注了。
至于那些通用dao,Service,Action的封装代码我就不贴了,因为我的上篇文章里已经贴过了,如果有不清楚的请转到我上篇文章里去阅读下,请见谅哈。
由于我是以城市级联的经典案例来演示的,所以首先就是要设计实体,由于我不想把省市区分别设计3个实体,而我是把他们都看作字典数据,何为字典数据,举个例子说吧,类似于性别,血型,星座,省份这些数据,几乎都是固定不变的,他们就是字典数据,下面是我设计的字典数据实体类:
package com.yida.common.basiccode;
import java.io.Serializable;
/**
* 基础数据类(如省份城市、性别、血型、星座等)
* @author Lanxiaowei
*
*/
public class BasicCode implements Serializable{
/**
* 主键id
*/
private Long id;
/**
* 中文名称
*/
private String cname;
/**
* 英文名称
*/
private String ename;
/**
* 父id
*/
private Long parentId;
/**
* 基础数据分组代码
*/
private String code;
/**
* 基础数据值(同一分组下基础数据value保持唯一)
*/
private String value;
/**
* 备注
*/
private String memo;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public BasicCode(){}
}
然后就是BasicCode.hbm.xml编写,全部是基础属性,没有什么关联关系,直接贴代码了,没什么好说的:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping package="entity">
<class name="com.yida.common.basiccode.BasicCode" table="basicCode">
<!-- 映射属性 -->
<id name="id" column="id" type="java.lang.Long">
<!--
主键值生成方式
native,identity自增
assigned 编程给定
increment hibernate使用select max(主键列)+1的方式生成
-->
<generator class="identity"></generator>
</id>
<!-- 基本属性 -->
<property name="cname" column="cname" type="java.lang.String" length="30" not-null="true"></property>
<property name="ename" column="ename" type="java.lang.String" length="30" not-null="true"></property>
<property name="parentId" column="parentId" type="java.lang.Long"></property>
<property name="code" column="code" type="java.lang.String" length="20" not-null="true"></property>
<property name="value" column="value" type="java.lang.String" length="20" not-null="true"></property>
<property name="memo" column="memo" type="java.lang.String" length="500" not-null="true"></property>
</class>
</hibernate-mapping>
然后就是BasicCode的dao/service/action编写,直接上代码,不清楚的看代码里的注释:
package com.yida.common.basiccode;
import java.util.ArrayList;
import java.util.List;
import com.yida.common.GerneralDaoImpl;
import com.yida.utils.Hql;
import com.yida.utils.Where;
public class BasicCodeDaoImpl extends GerneralDaoImpl<Long,BasicCode> implements IBasicCodeDao{
/**
* 加载所有的基础数据列表
* @return
*/
public List<BasicCode> findAllBasicCode(){
return findAll(BasicCode.class);
}
/**
* 基础数据组合查询
* @param basicCode 查询参数对象,
* 若传入对象为null则返回null
* 若参数对象属性值全部为null,则查询所有
* @return
*/
public List<BasicCode> findBasicCodeList(BasicCode basicCode){
if(null == basicCode){
return null;
}
Hql hql = Hql.start("from BasicCode b")
.addWhere("where", Where.get()
.equal("b.id", basicCode.getId())
.equal("b.parentId", basicCode.getParentId())
.equal("b.code", basicCode.getCode())
.equal("b.cname", basicCode.getCname())
.equal("b.ename", basicCode.getEname())
.equal("b.value", basicCode.getValue()));
return findByHql(hql.getSqlstr(), hql.getParams());
}
}
BasicCodeServiceImpl 实现类,接口代码我就不贴了。
package com.yida.common.basiccode;
import java.util.List;
import com.yida.common.GerneralServiceImpl;
public class BasicCodeServiceImpl extends GerneralServiceImpl implements IBasicCodeService{
/**
* 加载所有的基础数据列表
* @return
*/
public List<BasicCode> findAllBasicCode(){
return getBasicCodeDao().findAllBasicCode();
}
/**
* 基础数据组合查询
* @param basicCode 查询参数对象,
* 若传入对象为null则返回null
* 若参数对象属性值全部为null,则查询所有
* @return
*/
public List<BasicCode> findBasicCodeList(BasicCode basicCode){
return getBasicCodeDao().findBasicCodeList(basicCode);
}
}
由于是Aajx请求,所以我采用喜好的json方式返回数据给前端;下面是我为简化后台返回json数据做的一些封装:
package com.yida.common.json;
import java.io.Serializable;
import com.yida.common.Globarle;
/**
* Json模型类
* @author Lanxiaowei
* @createTime 2011-5-8 下午03:35:42
*/
public class JSONModel implements Serializable{
/**
* 总记录数属性名
*/
public static final String TOTAL_FIELD = "total";
/**
* 返回Json数组属性名
*/
public static final String ROOT_FIELD = "data";
/**
* 访问后台执行成功与否属性名
*/
public static final String SUCCESS_FIELD = "success";
/**
* 返回后台返回提示信息属性名
*/
public static final String MESSAGE_FIELD = "msg";
/**
* 总记录数
*/
private int totalCount;
/**
* 开始记录索引
*/
private transient int start;
/**
* 每页显示条数
*/
private transient int limit;
/**
* 执行非Query操作返回的提示信息
*/
private transient String message;
/**
* 是否执行成功
*/
private boolean success;
/**
* 执行动作类型(CRUD)
*/
private transient boolean queryAction;
/**
* 返回的Json字符串
*/
private String jsonString;
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
if(limit == 0){
limit = Globarle.PAGESIZE;
}
this.limit = limit;
}
public String getJsonString() {
return jsonString;
}
public void setJsonString(String jsonString) {
this.jsonString = jsonString;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean isQueryAction() {
return queryAction;
}
public void setQueryAction(boolean queryAction) {
this.queryAction = queryAction;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
JsonUtils工具类:
package com.yida.common.json;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import com.yida.common.Globarle;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.util.PropertyFilter;
/**
* Json工具类
* @author Lanxiaowei
* @createTime 2011-5-8 下午03:06:51
*/
public class JsonUtils {
/**
* List转换成Json字符串
*/
public static String listToJson(List list,final String[] filters){
if(null == list || list.size() == 0){
return "";
}
JSONArray jsonArray = null;
JsonConfig config = new JsonConfig();
config.registerJsonValueProcessor(
Date.class,
new DateJsonValueProcessor(Globarle.DEFAULT_DATE_FORMAT)
);
if(null != filters && filters.length != 0){
config.setJsonPropertyFilter(new PropertyFilter(){
public boolean apply(Object source, String name, Object value) {
for (String field : filters) {
if(name.equals(field)){
return true;
}
if(value instanceof Set && ((Set)value).size() == 0){
return true;
}
if(value instanceof Object[] && ((Object[])value).length == 0){
return true;
}
}
return false;
}
});
jsonArray = JSONArray.fromObject(list,config);
}
else{
jsonArray = JSONArray.fromObject(list,config);
}
return jsonArray.toString();
}
/**
* Object转换成Json字符串
* @param object 源对象
* @param ignorFields 忽略属性数组
* @return
*/
public static String objectToJson(Object object,final String[] ignorFields){
if(null == object){
return "";
}
JSONObject jsonObject = null;
if(null != ignorFields && ignorFields.length != 0){
JsonConfig config = new JsonConfig();
config.setJsonPropertyFilter(new PropertyFilter(){
public boolean apply(Object source, String name, Object value) {
for (String field : ignorFields) {
if(name.equals(field)){
return true;
}
if(value instanceof Set && ((Set)value).size() == 0){
return true;
}
if(value.getClass().isArray() && ((Object[])value).length == 0){
return true;
}
}
return false;
}
});
jsonObject = JSONObject.fromObject(object, config);
}
else{
jsonObject = JSONObject.fromObject(object);
}
return jsonObject.toString();
}
/**
* 输出Json字符串到客户端
* @param jsonModel json模型
* @param response 输出对象
*/
public static void outputJsonString(JSONModel model,HttpServletResponse response) throws IOException{
StringBuffer sb = new StringBuffer();
if(!model.isQueryAction()){
sb.append("{").append(model.SUCCESS_FIELD).append(":")
.append(model.isSuccess()).append(",").append(model.MESSAGE_FIELD).append(":'")
.append(model.getMessage()).append("'}");
}
else{
sb.append("{").append(model.TOTAL_FIELD).append(":").append(model.getTotalCount())
.append(",").append(model.ROOT_FIELD).append(":").append(model.getJsonString()).append("}");
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/x-json");
//response.setContentType("text/html");
response.getWriter().write(sb.toString());
}
}
package com.yida.common.json;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.yida.common.GerneralSessionAction;
/**
* ExtJson的Action基类(需要返回json数据的Action继承此类)
* @author Lanxiaowei
* @createTime 2011-5-8 下午03:33:39
*/
public abstract class ExtJSONActionSuport<T> extends GerneralSessionAction<T>{
/**
* 返回Json数据
* 使用说明:1. 需事先设置JsonModel的动作类型queryAction是Query(查询)还是非Query
* 2. 若为非Query操作需设置success属性,表明执行结果true/false,以及返回给页面的提示信息message
* 3. 若为Query操作,需设置总记录数totalCount
* 4. 不需要过滤属性请设为null
* @author Lanxiaowei
* @param list 对象集合
* @param filters 忽略属性数组(被忽略的属性将不会被Json-lib序列化)
* @param model json模型
* @param response 输出对象
* @return
* @throws Exception
*/
public String jsonExecute(List list,String[] filters,JSONModel model,HttpServletResponse response) throws Exception {
model.setJsonString(JsonUtils.listToJson(list,filters));
JsonUtils.outputJsonString(model, response);
return null;
}
}
package com.yida.common.json;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.sf.json.JsonConfig;
import net.sf.json.processors.JsonValueProcessor;
/**
* Json日期格式化接口
* @author Lanxiaowei
* @createTime 2011-5-14 下午09:04:52
*/
public class DateJsonValueProcessor implements JsonValueProcessor {
public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
private DateFormat dateFormat;
/**
* 构造方法.
*
* @param datePattern 日期格式
*/
public DateJsonValueProcessor(String datePattern) {
try {
dateFormat = new SimpleDateFormat(datePattern);
} catch (Exception ex) {
dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
}
}
public Object processArrayValue(Object value, JsonConfig jsonConfig) {
return process(value);
}
public Object processObjectValue(String key, Object value,
JsonConfig jsonConfig) {
return process(value);
}
private Object process(Object value) {
if (value==null) {
return "";
}else if (value instanceof Date) {
return dateFormat.format((Date)value);
}else {
return value;
}
}
}
DateJsonValueProcessor 类是用于json格式化java里的util.date对象,这里暂用不到,不过这个类很重要,json-lib是不能直接格式化util.Date对象的,这个是我自己编写的一个Date对象转换器,以后会用得到。
上面的都是写准备工作,现在开始切入正题,首先我们需要把下拉框抽象成java里的一个类,然后每次我们都是把我们抽象出来的这个下拉框类转换成json对象,然后前端解析json对象,动态去构建select的option,就是这个思路。
package com.yida.common.select;
import java.io.Serializable;
/**
* 下拉框对象(用于传递数据)
* @author Lanxiaowei
*
*/
public class SelectBO implements Serializable{
/**
* 当前下拉框页面元素id
*/
private String id;
/**
* 下拉框显示值
*/
private String display;
/**
* 下拉框value值
*/
private String value;
/**
* 上级下拉框Value对应下级属性名
*/
private String preProperty;
/**
* 上级下拉框value值
*/
private String preValue;
/**
* 当前下拉框数据的实体名称(含包名)
*/
private String entity;
/**
* 当前下拉框显示值对应实体类的属性名
*/
private String keyField;
/**
* 当前下拉框实际提交值对应实体类的属性名
*/
private String valueField;
/**
* 额外传参,如id=1&name=zhangsan(可选)
*/
private String params;
/**
* 用于下拉框数据排序,如 id desc(可选)
*/
private String orderBy;
/**
* 是否需要添加一个【---请选择---】的空选项,默认为true(可选)
*/
private boolean needEmptySelect = true;
/**
* 下拉框的行内样式
*/
private String style;
/**
* 下拉框应用的类样式
*/
private String cssClass;
/**
* 是否多选,默认false(可选)
*/
private boolean multiple;
/**
* 设置下拉框默认显示哪一项(可选)
*/
private String defaultValue="";
/**
* 设置显示语言(用于国际化)
*/
private String language = "zh";
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getPreProperty() {
return preProperty;
}
public void setPreProperty(String preProperty) {
this.preProperty = preProperty;
}
public String getPreValue() {
return preValue;
}
public void setPreValue(String preValue) {
this.preValue = preValue;
}
public String getEntity() {
return entity;
}
public void setEntity(String entity) {
this.entity = entity;
}
public String getKeyField() {
return keyField;
}
public void setKeyField(String keyField) {
this.keyField = keyField;
}
public String getValueField() {
return valueField;
}
public void setValueField(String valueField) {
this.valueField = valueField;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getOrderBy() {
return orderBy;
}
public void setOrderBy(String orderBy) {
this.orderBy = orderBy;
}
public boolean isNeedEmptySelect() {
return needEmptySelect;
}
public void setNeedEmptySelect(boolean needEmptySelect) {
this.needEmptySelect = needEmptySelect;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
public String getCssClass() {
return cssClass;
}
public void setCssClass(String cssClass) {
this.cssClass = cssClass;
}
public boolean isMultiple() {
return multiple;
}
public void setMultiple(boolean multiple) {
this.multiple = multiple;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public SelectBO(String display, String value) {
this.display = display;
this.value = value;
}
public SelectBO(){}
public SelectBO(String display, Long id) {
this.display = display;
this.value = id.intValue() + "";
}
}
然后就是selectDao,selectService,SelectAction的编写:同样的相关接口代码我就省了
package com.yida.common.select;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.beanutils.BeanUtils;
import com.yida.common.GerneralDaoImpl;
import com.yida.utils.Hql;
import com.yida.utils.Where;
/**
* 下拉框数据Dao接口实现类
* @author Lanxiaowei
*
*/
@SuppressWarnings("unchecked")
public class SelectDaoImpl extends GerneralDaoImpl<Long,SelectBO> implements ISelectDao {
/**
* 下拉框数据查询
* @param selectBO
* @return
*/
public List<SelectBO> getSelectList(SelectBO selectBO) throws Exception{
if(null == selectBO){
return null;
}
StringBuffer start = new StringBuffer("select new com.yida.common.select.SelectBO(s.");
start.append(selectBO.getKeyField()).append(",s.");
start.append(selectBO.getValueField()).append(") from ").append(selectBO.getEntity()).append(" as s");
Object preValue = null;
try {
preValue = new Long(selectBO.getPreValue());
} catch (Exception e) {
preValue = selectBO.getPreValue();
}
Where where = Where.get()
.equal("s."+selectBO.getValueField(), selectBO.getValue())
.equal("s." + selectBO.getPreProperty(), preValue);
String params = selectBO.getParams();
List pName = new ArrayList();
List pValue = new ArrayList();
if(null != params && params.length() > 0){
String[] s1 = params.split("&");
for (int i = 0; i < s1.length; i++) {
if(s1[i].equals("")){
continue;
}
pName.add(s1[i].split("=")[0]);
pValue.add(s1[i].split("=")[1]);
}
}
if(pName.size() > 0 && pValue.size() > 0){
for (int i = 0; i < pName.size(); i++) {
where.equal("s." + pName.get(i), pValue.get(i));
}
}
Hql hql = Hql.start(start.toString()).addWhere("where", where);
if(null != selectBO.getOrderBy() && !selectBO.getOrderBy().equals("")){
hql.orderby("s."+selectBO.getOrderBy());
}
List<SelectBO> list = (List<SelectBO>)findByHql(hql.getSqlstr(), hql.getParams());
if(null == list || list.size() == 0){
return null;
}
List<SelectBO> selects = new ArrayList<SelectBO>();
for (SelectBO select : list) {
String display = BeanUtils.getProperty(select, "display");
String value = BeanUtils.getProperty(select, "value");
BeanUtils.copyProperties(select, selectBO);
select.setDisplay(display);
select.setValue(value);
selects.add(select);
}
return selects;
}
public static void main(String[] args) {
String params = "name=zhangsan";
String[] s1 = params.split("&");
for (int i = 0; i < s1.length; i++) {
System.out.println(s1[i]);
}
System.out.println(s1.length);
}
}
package com.yida.common.select;
import java.util.List;
import com.yida.common.GerneralServiceImpl;
public class SelectServiceImpl extends GerneralServiceImpl implements ISelectService{
/**
* 下拉框数据查询
* @param selectBO
* @return
*/
public List<SelectBO> getSelectList(SelectBO selectBO) throws Exception{
return getSelectDao().getSelectList(selectBO);
}
}
package com.yida.common.select;
import com.yida.common.json.JSONModel;
/**
* @author Lanxiaowei
* @createTime 2011-11-27 上午10:45:00
*/
public class SelectModel extends JSONModel{
private SelectBO selectBO;
public SelectBO getSelectBO() {
if(null == selectBO){
selectBO = new SelectBO();
}
return selectBO;
}
public void setSelectBO(SelectBO selectBO) {
this.selectBO = selectBO;
}
}
package com.yida.common.select;
import java.util.List;
import com.yida.common.json.ExtJSONActionSuport;
/**
* 下拉框Action
* @author Lanxiaowei
*
*/
@SuppressWarnings("unchecked")
public class SelectAction extends ExtJSONActionSuport<SelectModel>{
private static String[] filters = new String[]{
"preProperty","preValue","entity",
"keyField","valueField","params",
"orderBy","style","cssClass","language"
};
/**
* 用于返回Select下拉框Json数据
*/
public String getSelectOptionData(){
SelectModel selectModel = this.getModel();
selectModel.setQueryAction(true);
try {
List<SelectBO> selectBOList = getSelectService().getSelectList(this.getModel().getSelectBO());
if(null != selectBOList){
selectModel.setTotalCount(selectBOList.size());
}
jsonExecute(selectBOList, filters, selectModel, getResponse());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
到此后台就写好了,接下来新建个jsp页面,导入xmlhttprequest.js,xmlhttprequest.js里其实就是一个兼容各种浏览器的获取xmlHttpRequest对象的通用函数,如果你使用jqery类库的话,就不必导入它。
//创建xmlHttpRequest对象
var xmlHttp;
function createXmlHttpRequest(){
if(window.ActiveXObject) {
//如果是IE,循环遍历版本
var xmlHttps = ["MSXML2.XMLHttp.5.0","MSXML2.XMLHttp4.0","MSXML2.XMLHttp3.0","MSXML2.XMLHttp","Microsoft.XMLHTTP"];
for(var i = 0; i < xmlHttps.length; i++)
{
try{
var xl = new ActiveXObject(xmlHttps[i]);
return xl;
}catch(error){
throw new Error("Create XmlHttpRequest failure!");
}
}
} else {
//火狐等其他浏览器
var xl = new XMLHttpRequest();
return xl;
}
}
然后你在页面定义3个空的select元素,分别给他们取个id便于接下来检索他们。然后就是通用的发送ajax请求获取数据的函数和动态添加option的函数,他们都是通用的,如果你的项目也是基于SSH环境搭建,那你只管把这几个函数copy过去不需要做任何修改,你甚至可以把他们独立一个js文件里再引入,这样页面显得更清爽些。下面是的selectTest.jsp源码:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path;
request.setAttribute("basePath",basePath);
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'selectTest.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript" src="${basePath}/js/xmlhttprequest.js"></script>
<style type="text/css">
#test{
width:320px;
height:200px;
position:absolute;
left:50%;
margin-left:-150px;
top:100px;
font-size:12px;
text-align:center;
}
select {
boder-right: #666666 2px solid;
border-top: #666666 2px solid;
padding-left: 5px;
font-size: 12px;
z-index: 100;
padding-bottom: 0px;
margin: 0px;
border-left: #666666 2px solid;
padding-top: 0px;
border-bottom: #666666 2px solid;
white-space: pre;
top: 14px;
width:80px;
height:20px;
background-color: #ffffff;
text-align: left;
text-decoration: none;
}
</style>
</head>
<body>
<div id="test">
<select id="pro" onchange="sendRequest('city','com.yida.common.basiccode.BasicCode','cname','id','parentId',this.value,'code=city')"></select>省
<select id="city" onchange="sendRequest('district','com.yida.common.basiccode.BasicCode','cname','id','parentId',this.value,'code=district')"></select>市
<select id="district"></select>区(县)
</div>
<script type="text/javascript">
/*
id: 当前下拉框id
entity: 下拉框数据的实体名称(含包名)
keyField: 下拉框显示值对应实体类的属性名
valueField: 下拉框实际提交值对应实体类的属性名
preProperty: 上级下拉框value值对应实体属性名
preValue: 上级下拉框的value值,用作级联的过滤条件
params: 如&id=1&name=zhangsan
*/
function sendRequest(id,entity,keyField,valueField,preProperty,preValue,params){
var url = "${basePath}/selectAction!getSelectOptionData.do?selectBO.id=" + id
+ "&selectBO.entity=" + entity + "&selectBO.keyField=" + keyField + "&selectBO.valueField=" + valueField;
if(preProperty.length > 0 && preValue.length > 0){
url += "&selectBO.preProperty=" + preProperty + "&selectBO.preValue=" + preValue;
}
if(params.length > 0){
url += "&selectBO.params=" + params;
}
var xmlHttp = createXmlHttpRequest();
xmlHttp.open('get', url, false);
xmlHttp.send(null);
if (xmlHttp.readyState == 4){
if (xmlHttp.status == 200){
var rText = eval("(" + xmlHttp.responseText + ")");
var data = rText.data;
if(data.length > 0){
var selectObj = document.getElementById(id);
//添加前先清空option
selectObj.innerHTML = "";
var flag = false; //是否插入空选项
if(data[0].needEmptySelect){
selectObj.add(new Option("--请选择--",""),0);
flag = true;
}
selectObj.value = data[0].defaultValue;
for(var i = 0; i < data.length; i++){
var display = data[i].display;
var val = data[i].value;
if(flag){
addOption(selectObj,display,val,i+1);
} else{
addOption(selectObj,display,val,i);
}
}
}
}
}
}
/*
select: 下拉框对象
display: option的显示值
val: option的提交值
pos: option的索引位置
*/
function addOption(select,display,val,pos){
var objOption = new Option(display,val);
if (pos == -1 & pos > select.options.length){
select.options[select.options.length] = objOption;
}
else{
select.add(objOption, pos);
}
}
window.onload = function(){
sendRequest("pro","com.yida.common.basiccode.BasicCode","cname","id","","","code=province");
}
</script>
</body>
</html>
window.onload 是用于页面加载完成后ajax请求后台去初始化第一个下拉框的数据。至于sendRequest函数的各参数说明,我代码里已经有做详细解释,就不啰嗦了,请仔细阅读代码哈!
最后贴下我数据库插入测试数据的sql代码吧,也方便你们在本地测试:
/*
Navicat MySQL Data Transfer
Source Server : localhost_3306
Source Server Version : 50136
Source Host : localhost:3306
Source Database : test
Target Server Type : MYSQL
Target Server Version : 50136
File Encoding : 65001
Date: 2011-11-27 17:41:31
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `basiccode`
-- ----------------------------
DROP TABLE IF EXISTS `basiccode`;
CREATE TABLE `basiccode` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`cname` varchar(30) NOT NULL,
`ename` varchar(30) NOT NULL,
`code` varchar(20) NOT NULL,
`value` varchar(20) NOT NULL,
`memo` longtext NOT NULL,
`parentId` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of basiccode
-- ----------------------------
INSERT INTO `basiccode` VALUES ('1', '湖北', 'hubei', 'province', '01', '湖北省', null);
INSERT INTO `basiccode` VALUES ('2', '湖南', 'hunan', 'province', '02', '湖南省', null);
INSERT INTO `basiccode` VALUES ('3', '福建', 'fujian', 'province', '03', '福建省', null);
INSERT INTO `basiccode` VALUES ('4', '武汉', 'wuhan', 'city', '01', '武汉市', '1');
INSERT INTO `basiccode` VALUES ('5', '黄冈', 'huanggang', 'city', '02', '黄冈市', '1');
INSERT INTO `basiccode` VALUES ('6', '荆州', 'jingzhou', 'city', '03', '荆州市', '1');
INSERT INTO `basiccode` VALUES ('7', '长沙', 'changsha', 'city', '04', '长沙市', '2');
INSERT INTO `basiccode` VALUES ('8', '湘潭', 'xiangtan', 'city', '05', '湘潭市', '2');
INSERT INTO `basiccode` VALUES ('9', '衡阳', 'hengyang', 'city', '06', '衡阳市', '2');
INSERT INTO `basiccode` VALUES ('10', '厦门', 'xiamen', 'city', '07', '厦门市', '3');
INSERT INTO `basiccode` VALUES ('11', '福州', 'fuzhou', 'city', '08', '福州市', '3');
INSERT INTO `basiccode` VALUES ('12', '泉州', 'quanzhou', 'city', '09', '泉州市', '3');
INSERT INTO `basiccode` VALUES ('13', '江岸', 'jiangan', 'district', '01', '江岸区', '4');
INSERT INTO `basiccode` VALUES ('14', '江汉', 'jianghan', 'district', '02', '江汉区', '4');
INSERT INTO `basiccode` VALUES ('15', '硚口', 'qiaokou', 'district', '03', '硚口区', '4');
INSERT INTO `basiccode` VALUES ('16', '武昌', 'wuchang', 'district', '04', '武昌区', '4');
INSERT INTO `basiccode` VALUES ('17', '新洲', 'xinzhou', 'district', '05', '新洲区', '4');
INSERT INTO `basiccode` VALUES ('18', '黄州', 'huangzhou', 'district', '06', '黄州区', '5');
INSERT INTO `basiccode` VALUES ('19', '武穴', 'wuxue', 'district', '07', '武穴市', '5');
INSERT INTO `basiccode` VALUES ('20', '蕲春', 'qichun', 'district', '08', '蕲春县', '5');
INSERT INTO `basiccode` VALUES ('21', '沙市', 'shashi', 'district', '09', '沙市区', '6');
INSERT INTO `basiccode` VALUES ('22', '荆州', 'jingzhou', 'district', '10', '荆州区', '6');
INSERT INTO `basiccode` VALUES ('23', '洪湖', 'honghu', 'district', '11', '洪湖区', '6');
INSERT INTO `basiccode` VALUES ('24', '江陵', 'jiangling', 'district', '12', '江陵县', '6');
INSERT INTO `basiccode` VALUES ('25', '芙蓉', 'furong', 'district', '13', '芙蓉区', '7');
INSERT INTO `basiccode` VALUES ('26', '天心', 'tianxin', 'district', '14', '天心区', '7');
INSERT INTO `basiccode` VALUES ('27', '岳麓', 'yuelu', 'district', '15', '岳麓区', '7');
INSERT INTO `basiccode` VALUES ('28', '开福', 'kaifu', 'district', '16', '开福区', '7');
INSERT INTO `basiccode` VALUES ('29', '雨花', 'yuhua', 'district', '17', '雨花区', '7');
INSERT INTO `basiccode` VALUES ('30', '雨湖', 'yuhu', 'district', '18', '雨湖区', '8');
INSERT INTO `basiccode` VALUES ('31', '岳塘', 'yuetang', 'district', '19', '岳塘区', '8');
INSERT INTO `basiccode` VALUES ('32', '珠晖', 'zhuhui', 'district', '20', '珠晖区', '9');
INSERT INTO `basiccode` VALUES ('33', '雁峰', 'yanfeng', 'district', '21', '雁峰区', '9');
INSERT INTO `basiccode` VALUES ('34', '石鼓', 'shigu', 'district', '22', '石鼓区', '9');
INSERT INTO `basiccode` VALUES ('35', '蒸湘', 'zhengxiang', 'district', '23', '蒸湘区', '9');
INSERT INTO `basiccode` VALUES ('36', '南岳', 'nanyue', 'district', '24', '南岳区', '9');
INSERT INTO `basiccode` VALUES ('37', '湖里', 'huli', 'district', '25', '湖里区', '10');
INSERT INTO `basiccode` VALUES ('38', '思明', 'siming', 'district', '26', '思明区', '10');
INSERT INTO `basiccode` VALUES ('39', '集美', 'jimei', 'district', '27', '集美区', '10');
INSERT INTO `basiccode` VALUES ('40', '同安', 'tongan', 'district', '28', '同安区', '10');
INSERT INTO `basiccode` VALUES ('41', '海沧', 'haicang', 'district', '29', '海沧区', '10');
INSERT INTO `basiccode` VALUES ('42', '翔安', 'xiangan', 'district', '30', '翔安区', '10');
INSERT INTO `basiccode` VALUES ('43', '鼓楼', 'gulou', 'district', '31', '鼓楼区', '11');
INSERT INTO `basiccode` VALUES ('44', '台江', 'taijiang', 'district', '32', '台江区', '11');
INSERT INTO `basiccode` VALUES ('45', '晋安', 'jinan', 'district', '33', '晋安区', '11');
INSERT INTO `basiccode` VALUES ('46', '仓山', 'cangshan', 'district', '34', '仓山区', '11');
INSERT INTO `basiccode` VALUES ('47', '马尾', 'mawei', 'district', '35', '马尾区', '11');
INSERT INTO `basiccode` VALUES ('48', '琅岐', 'langqi', 'district', '36', '琅岐区', '11');
INSERT INTO `basiccode` VALUES ('49', '丰泽', 'fengze', 'district', '37', '丰泽区', '12');
INSERT INTO `basiccode` VALUES ('50', '鲤城', 'licheng', 'district', '38', '鲤城区', '12');
INSERT INTO `basiccode` VALUES ('51', '洛江', 'luojiang', 'district', '39', '洛江区', '12');
INSERT INTO `basiccode` VALUES ('52', '泉港', 'quangang', 'district', '40', '泉港区', '12');
INSERT INTO `basiccode` VALUES ('53', '石狮', 'shishi', 'district', '41', '石狮市', '12');
INSERT INTO `basiccode` VALUES ('54', '晋江', 'jinjiang', 'district', '42', '晋江市', '12');
再贴张我数据库表数据查询后的截图:

到此代码就全部贴完了,如果还有疑问的,请在下方评论,如果我有哪里写的不好,或者还有哪里需要改进,也请你在下方评论指正,非常欢迎大家多多拍砖!没有最好,只有更好!也非常感谢我的那些忠实fans们,我会一如既往的写下去,谢谢你们一直来的支持!
本文来源 我爱IT技术网 http://www.52ij.com/jishu/94.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)

不完整,有的方法没有贴出来