1. 程式人生 > >JQuery中使用select2外掛功能之自動完成下拉框動態載入匹配(本地資料與遠端資料結合)

JQuery中使用select2外掛功能之自動完成下拉框動態載入匹配(本地資料與遠端資料結合)

前言
最近專案中遇到一個問題:前端載入頁面的時候某些頁面載入速度很慢,耗時很久;有些頁面載入的飛快;因為上面所說的頁面都是由同一個程式動態生成的,利用多執行緒同步機制完成不同的前端頁面;因此當時就斷定不是程式的大問題;應該是處理資料部分存在問題;在chrome瀏覽器一測;還真的是大資料的問題;進入頁面就載入大批量資料進來;不僅增加資料庫的負擔 ,前後端處理資料也會造成一定的壓力;因為有些函式把這些大資料遍歷掉了;耗時非常大。最終找到多個造成此問題的原因;現在就拿其中的一個出來說說;剛好涉及到最新版本的select2外掛的一些功能;就如何在select2下拉框大資料的情況下處理以達到流暢、無壓力的狀態。
一、設計思路


進入前端頁面,對於select2下拉框的資料來源;先載入一很小部分資料作為本地資料,先做個簡單的分頁功能;進來頁面之後,點選下拉框載入顯示就是本地資料;當在下拉框輸入值搜尋的時候在本地資料中搜索不到就去遠端載入資料;這裡說一下,select2原本是有搜尋匹配和遠端資料這兩個功能的,官方文件也有說明;不過因為這兩個功能是相對獨立的;所以在搜尋匹配的時候我對select2的matcher匹配功能方法做了重構;遠端功能的方法也是自己寫的方法;做法是這樣的:當輸入值搜尋的時候,先匹配本地資料,如果匹配本地資料最後一個都沒有合適的就遠端訪問資料庫抓匹配資料,去後臺抓的話也是載入一部分資料,做分頁功能;返回的資料又作為本地資料疊加;退出頁面將重新開始,重新載入。解決這些問題所遇到的坑這裡就不一一說了,我已經填好坑了;直接走過去就行了。
二、實現效果(最左匹配實現)

在這裡插入圖片描述
三、程式碼例項
1.前端部分
1.1 HTML程式碼

部分html頁面程式碼,設定div區域,id=“tab2ModalBody”

<div class="box box-primary">
	<form class="form-horizontal" role="form" id="foo">
		<div class="box-body" style="padding-top: 0px;padding-bottom: 0px;">
			<div class="box-body" id="tab2ModalBody" style="padding-bottom: 0px;">
			</div>
		</div>
	</form>
</div>

1.2 JS程式碼
(1)動態載入html頁面內容的function;動態拼接html,及繫結select2事件

       function SetModalField(data,data5) {//data為初始化頁面時載入進來的資料
                    var res=[];
                    var selectData = "<option value=\"\"> </option>";// 初始化<option>標籤
                        defData = data[i].DATA_DEFAULT.split(";");// 分解初始化資料
                        for (var b = 0; b < defData.length; b++) {//遍歷顯示的資料
                            selectData += "<option value=\"" + defData[b] + "\">" + defData[b] + "</option>";
                        }
                     fieldTypeString = "<select id=\"" + data[i].TABLE_FIELD + "\"  +
                        "class=\"form-control select tableData sel2\"  
                        " >" + selectData + "</select> ";//將option值拼接在select標籤
                        //將上面拼接的html程式碼嵌入到id=tab2ModalBody的錨下面
                        $("#tab2ModalBody").html(fieldString);
                        //定位sel2結尾的class,就是form-control select tableData sel2的class在jquery中使用select2
                        $(".sel2").select2({
			        theme: 'bootstrap',//主題
			        allowClear: true,//允許清除
			        matcher:matchStart,//自定義匹配功能,呼叫matcher函式
			    }).on("select2:open", function () {//監聽下拉框被開啟事件
			    	select2count=0;//先初始化一些引數,匹配時需要用到
			    	select2times=0;
			    	currentSelect2Id=this.id;
			        selectChange(this.id);//事件函式
			    });
   }

(2)當在搜尋框輸入值做匹配時,會呼叫對應的下面matcheStart(param,data)函式進行將輸入框的值跟本地資料一個個去做匹配;演算法可以自定義,下面的匹配功能是最左匹配功能,即從最左開始匹配,有符合的就返回給下拉框顯示,注意:params為輸入的引數,params.term為輸入框的值;data一次次遍歷下拉框顯示的值,一次只傳一個,下拉框有多少值就遍歷傳多少次。

    function matchStart(params, data) {//遍歷一次次匹配
      // If there are no search terms, return all of the data
      if ($.trim(params.term) === '') {
        return data; 
      }
      // Skip if there is no 'text' property
      if (typeof data.text === 'undefined') {
        return null;
      }
      // `data.children` contains the actual options that we are matching against
      select2times++;//標誌位從0開始 +1,若匹配本地資料的次數是最後一次的話就去後臺請求
      var currentSelect2Qty=($("#"+currentSelect2Id+"").children('option').length);//獲取當前下拉框資料總數量n筆
      if(select2times>=currentSelect2Qty){//最後一次進來匹配本地資料
	//先做最後一次匹配
	 if (data.text.toUpperCase().indexOf(params.term.toUpperCase()) == 0) {
			   select2count++;
			  var modifiedData = $.extend({}, data, true);
			  select2times=0;
			  select2count=0;
			  return modifiedData;
		   }else{//如果最後都匹配不到資料,則去後臺請求資料
			   if(select2count==0){//標記位,匹配到資料的話,select2count不等於0
			     var option1=remoteSelct2Data(params.term);//呼叫遠端請求function;返回請求的資料
			     if(option1.length>0){//有值則取,清空標誌位,返回
				  var newModifiedData= $.extend({},data,option1[0]); //$.extend()合併函式
				  select2times=0;
				  select2count=0;
				  return newModifiedData;
			     }else{//清空標誌位,返回null
				  select2times=0;
				  select2count=0;
				  return null;//返回 
			     }

			   }else{//清空標誌位,返回null
				  select2times=0;
				  select2count=0;
				  return null;  
			   }

		   }
 	      }else{//不是最後一次進來匹配,  
	  if (data.text.toUpperCase().indexOf(params.term.toUpperCase()) == 0) {
		   select2count++;
		  var modifiedData = $.extend({}, data, true);
		   return modifiedData;
		   }else{
			   return null;
		   }
  }
}

(3)ajax遠端請求後臺資料,引數為搜尋框的值;傳遞的引數可以自定義。

function remoteSelct2Data(term){	   
	   var jsonparam={
				'term':term,
				'page':pagesize  //pagesize為全域性變數,引數可以自己自行增加定義
		   }; 	   
		   $.ajax({
               type: "POST",
               url: '/PostServlet',
               data: JSON.stringify({
                   funCode: funCode,//全域性引數,自行定義
                   funData: JSON.stringify({
                       func: 'getSelect2Data',
                       paramData: jsonparam,
                       schema:schema
                   })
               }),
               dataType: "json",//返回的資料是json格式
               async: false,
               success: function (data) {  //遠端請求返回的資料data,
                if(data[0].EX == 'NG'){
             	   data=data.splice(1,data.length);
                	   for(var i=0;i<data.length;i++){
                	           //遍歷返回的資料,新增到id=currentSelect2Id的select標籤元素下面的option值
 	        			$("#"+currentSelect2Id+"").append("<option value='"+data[i].id+"'>"+data[i].text+"</option>");
 	        			options.push({ id : data[i].id,text : data[i].text});
    	        		}
                }
               
               }
           }); 
		   return options;//返回值
       } 
 }	

2.後端部分
2.1後臺接受到請求後分發執行的方法doFunction;上一層這裡就忽略了;

public String doFunction(FunctionPojo funRequest,//FunctionPojo 自定義的bean,裡面包含需要的引數
		HttpServletResponse response) throws Exception {
	JSONObject jsonData = JSONObject.fromObject(funRequest.getFunData());
	String action = jsonData.getString("func");
	String schema=jsonData.getString("schema");
	switch (action) {
	case "getSelect2Data":
		res=getselect2data(schema, jsonData);//執行具體方法
		break;
	}
	return res;
}

2.2 具體的訪問資料庫執行的方法

 public synchronized  String getselect2data(String schema,JSONObject jsondata){
		ResultSet rs1;
		JSONObject jsonParam=jsondata.getJSONObject("paramData");
		JSONObject json =new JSONObject();
		JSONArray jsonArr=new JSONArray();
		JSONObject jsonOra=new JSONObject();
		jsonOra.put("EX", "NG");
		String select2Sql=queryData.getRegSql(jsonParam);
				try {
					rs1=super.getResultSet(select2Sql, schema, new Object[]{});
					while(rs1.next()){
						if (rs1.getString(1) != null) {
							//System.out.println(rs1.getString(1));
							json.put("id",rs1.getString(1));
							json.put("text", rs1.getString(1));
						}
						jsonArr.add(json);	
					}
					jsonArr.add(0, jsonOra);
				} catch (SQLException e) {
					// TODO 自動生成 catch 塊
					jsonOra.put("EX", "異常資訊:"+e.getLocalizedMessage());
					jsonArr.add(0, jsonOra);
					e.printStackTrace();
				}
						
		return jsonArr.toString();
	}

2.3 這裡巢狀分頁功能的sql,半個分頁,沒有做分頁引數;應為原來就已經有了查詢資料的sql;所以做優化就要在原來的sql上面再巢狀一層sql,好做分頁;至於具體的sql分頁效率,可以參考最有效的sql分頁

public static synchronized String getRegSql(JSONObject jsonObj){
	String conditionId=jsonObj.getString("currentSelect2Id");
	String select2Sql=jsonObj.getString("currentSelect2DBSql");
	String selectTerm=jsonObj.getString("term");
	String empNo=jsonObj.getString("empNo");
	String prodAreaId=jsonObj.getString("prodAreaId");
	if(select2Sql!=" "&&!select2Sql.equals(" ")){
		select2Sql ="SELECT SEL2.* FROM ( " +select2Sql +"  )SEL2  WHERE ROWNUM<=10  AND   
		      SEL2."+conditionId+" LIKE '"+selectTerm+"%'";       
		}
	return select2Sql;
}