两级联动菜单的实现之一 - 用taglib实现上拉菜单

两级联动菜单的实现之一 --- 用taglib实现下拉菜单

最近在项目里有一个功能点需要实现页面的菜单联动,具体场景描述:页面有三个输入:接口名称、接口版本和接口参数,前两者是下拉菜单,第三个是input标签,接口名称需要从数据库表动态生成,接口版本跟随接口名称的变化而变化,接口参数根据接口名称和接口版本来确定内容。我决定用taglib实现一个标签来满足接口名称从后台数据库读取的要求,再用ajax技术来实现菜单联动的效果。

一、taglib重写select标签

      整个标签要实现上述功能,需要实现两个要点:首先当然是select标签的重写,其二就是操作数据库,提取满足需求的数据。

      标签代码如下:     

package com.vness.mytag;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator; 
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.TagSupport;

import com.vness.data.pullDownMenuData;

public final class tagSelectExt extends TagSupport  implements DynamicAttributes {
	/**
	 * 控件名称
	 */
	private String name;
	
	private String id;
	
	//数据库表名字
	private String dbTable;
 
	//值列
	private String valueCol;
	
	//展示列
	private String nameCol;
	
	//数据集合
	private HashMap paramMap = new HashMap() ;
	
	//默认选中
	private String defaultSelected;
	
	//需要支持的属性标签
	private String onChange;
	/**
	 * option style
	 * 0: 全部
	 * 1: 请选择
	 */
	private String optStyle; 
	
	public void setDynamicAttribute(String uri, String name, Object value) throws JspException
	  {
	    if ("id".equalsIgnoreCase(name)) {
	      setId((String)value);
	      return;
	    }
	     
	    if ("name".equalsIgnoreCase(name)) {
		      setName((String)value);
		      return;
		    }
	    
	    if ("dbTable".equalsIgnoreCase(name)) {
		      setDbTable((String)value);
		      return;
		    }
	    
	    if ("valueCol".equalsIgnoreCase(name)) {
		      setValueCol((String)value);
		      return;
		    }
	    
	    if ("nameCol".equalsIgnoreCase(name)) {
		      setNameCol((String)value);
		      return;
		    }
	    
	    if ("defaultSelected".equalsIgnoreCase(name)) {
		      setDefaultSelected((String)value);
		      return;
		    }
	    
	    if ("optStyle".equalsIgnoreCase(name)) {
		      setOptStyle((String)value);
		      return;
		    } 
	    
	    if ("onChange".equalsIgnoreCase(name)) {
		      setOnChange((String)value);
		      return;
		    } 
	  
	  } 
	 
	/*
	 * Setter Mothed Start!
	 */ 
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDbTable() {
		return dbTable;
	}

	public void setDbTable(String dbTable) {
		this.dbTable = dbTable;
	}

	public String getValueCol() {
		return valueCol;
	}

	public void setValueCol(String valueCol) {
		this.valueCol = valueCol;
	}

	public String getNameCol() {
		return nameCol;
	}

	public void setNameCol(String nameCol) {
		this.nameCol = nameCol;
	}

	public String getDefaultSelected() {
		return defaultSelected;
	}

	public void setDefaultSelected(String defaultSelected) {
		this.defaultSelected = defaultSelected;
	}

	public String getOptStyle() {
		return optStyle;
	}

	public void setOptStyle(String optStyle) {
		this.optStyle = optStyle;
	}
	
	public void setId(String id) {
		this.id = id;
	}
	
	public  void setOnChange(String value) {
		// TODO Auto-generated method stub
		this.onChange=value;
	}

	private  void setParamMap() throws Exception {
		 
		String condition=genSql();
		pullDownMenuData pdd=new pullDownMenuData();
		try {
			pdd.selectData(condition);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new Exception("查询数据失败!"+e);
		}
		
		paramMap=pdd.getData();   
		 
	} 
	
	/*
	 * Setter Mothed End!
	 */
	
	private String genId() { 
		if( id == null ) {
			 id = "select_" + Math.random();
		} 
		return id;
	}
	
	public String genSql(){ 
		StringBuffer buf = new StringBuffer();
		buf.append("select distinct ");
		buf.append(nameCol+", "+valueCol+" ");
		buf.append("from "+ dbTable);
		return buf.toString();
	}
	
	@Override
	public int doEndTag() throws JspException { 
		//查询数据库,获取数据
		try {
			setParamMap();
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		StringBuffer buf = new StringBuffer();

		buf.append("<select ");
		buf.append("id='" + genId() + "' ");
		if (!(name==null||name.equalsIgnoreCase(""))) {
			buf.append("name='" + name + "' ");
		}  
		
		if (!(onChange==null||onChange.equalsIgnoreCase(""))) {
			buf.append("onChange='" + onChange + "' ");
		}  
		
		buf.append(" >"); 
		
		if("0".equals(optStyle)){
			buf.append("<option value=''>全部</option>");
		}
		else if("1".equals(optStyle)){
			buf.append("<option value=''>请选择</option>");
		}
		
		if (paramMap != null) {
			Iterator iter = paramMap.keySet().iterator();
			while (iter.hasNext()) {
				String key = (String) iter.next();
				String value = (String) paramMap.get(key);
				buf.append("<option value='" + value + "'");
				
				if (value.equalsIgnoreCase(defaultSelected)) {
					buf.append(" selected ");
				}
				buf.append(" >" + key + "</option>");
				buf.append("\r\n"); 
			}
			buf.append("</select>");
		} 

		try {
			this.pageContext.getOut().write(buf.toString());
		} catch (IOException e) {
			throw new JspException(e);
		}
		name = null;
		dbTable = null;
		valueCol = null;
		nameCol = null; 
		if(paramMap != null ) {
			paramMap.clear();
		}
		return EVAL_PAGE;
	}  
	@Override
	public int doStartTag() throws JspException {
		return super.doStartTag();
	} 
}


     如果自定义标签要实现数据的动态变化,必须实现DynamicAttributes接口,重写setDynamicAttribute方法。该类的所有属性除了paramMap是作为从后台传递菜单内容到前端的数据载体外,其他属性都是标签定义的。

     我还特地实现了一个数据处理类,代码如下:     

package com.vness.data;
 
import java.sql.ResultSet; 
import java.util.HashMap;

import com.vness.tool.dbTool;

public  class pullDownMenuData{ 
	private HashMap<String, String> data;
	
	public void selectData(String sql) throws Exception{
		dbTool dt= new dbTool();
		dt.createConnection();
		ResultSet rs = dt.executeSQL(sql); 
		
		data=new HashMap<String, String>();
		while(rs.next()){   
		     String name = rs.getString(1) ;   
		     String value = rs.getString(2) ;  
                     data.put(name, value);
		 } 
	}
	
	public HashMap<String, String>  getData(){
		return data;
	}
	
}

整个类很简单,就是把数据从后台读取出来,放到map中,传递给标签类。

另外,还有一个数据库工具类,因为时间原因,实现的很简单,只实现了简单的连接、查询等功能。由于我实现的这个功能是内部的一个平台,不对外开发,并发量不大,所以也没用连接池,仅仅用了最简单的jdbc。如果并发高的话,建议使用别的方式实现。

package com.vness.tool;
 
import java.io.FileInputStream; 
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class dbTool {
	private Connection con;
	private ResultSet  result;
    private static  String url ;    
	private static  String username ;   
	 private  static  String password ;   
	
	static{
		try{   
		    //加载MySql的驱动类   
		    Class.forName("oracle.jdbc.driver.OracleDriver") ;   
		    }catch(ClassNotFoundException e){    
		    e.printStackTrace() ;   
		    }   
		
		try {
			loadInit() ;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private static void loadInit() throws Exception{
		   Properties prop = new Properties(); 
		   FileInputStream fis; 
		   fis = new FileInputStream("dbprop.properties");
		   prop.load(fis);  
		   url=prop.getProperty("url");
		   username=prop.getProperty("username");
		   password=prop.getProperty("password"); 
	}
	
	public Connection  createConnection() throws Exception{ 
		if(url==null||username==null||password==null){
			throw new Exception("database param is error!");
		}  
	    con =  DriverManager.getConnection(url , username , password ) ;    
	     return con;
	}
	
	public void closeConnection(){
		if(con!=null){
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public ResultSet executeSQL(String sql){
		  try { 
			   Statement stmt=con.createStatement();
			   result = stmt.executeQuery(sql) ;   
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   
		  return result;
	}
	
	public static void main (String[] x) throws Exception
	{
		dbTool dt= new dbTool();
		Connection con=dt.createConnection();
		ResultSet rs = dt.executeSQL("select api_id,api_cnm from mpoptapii  "); 
		 try {
			while(rs.next()){   
			     String name = rs.getString(1) ;   
			    String pass = rs.getString(2) ;   
			 }
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}   
		
		dt.closeConnection();
	}

}

二、标签使用的配置

最后是需要配置了,配置完了就可有使用标签了。配置主要包括三个方面:标签的定义声明、web.xml的声明和页面的引用。

hitag.tld文件:定义标签

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>
  <tlib-version>2.2.3</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>test</short-name>
   
 <tag> 
    <name>hiselect</name>
    <tag-class>com.vness.mytag.tagSelectExt</tag-class>
    <dynamic-attributes>true</dynamic-attributes>
 </tag> 
</taglib>

web.xml:声明标签,在web.xml的最后加入如下配置。

 <taglib>
     <taglib-uri>/WEB-INF/tlds/hitag.tld</taglib-uri>
     <taglib-location>/WEB-INF/tlds/hitag.tld</taglib-location>
 </taglib>

最后,就是页面的引入了。

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ taglib uri="/WEB-INF/tlds/hitag.tld" prefix="test" %>  
<html>
<head>
<script type="text/javascript">
 
</script>
</head>
<body> 
    <tr>  
          <td>接口名称</td> 
<td> 


  <test:hiselect name="tstNm" id="tst_nm" optStyle="1" dbTable="mpoptapii" valueCol="api_id" nameCol="api_cnm" defaultSelected="1"/> </td> 
 </tr>
</body>
</html>