1. 程式人生 > >JQuery datatables 重新載入新的action的url datatables ajax reload from new url(全網僅有的解決方案)

JQuery datatables 重新載入新的action的url datatables ajax reload from new url(全網僅有的解決方案)

      百度了很多,也谷歌了很久,很多人提出了類似的問題,卻沒有答案,好不容易找到一個解決的方式,所以有點興奮的寫下來,希望這個坑同樣有人一起跳。

    1.因為後臺的資料是很多的,所以需要進行分頁,這樣就需要拿到datatables傳給後臺的分頁資訊,查詢完資料之後也需要將資料分頁資訊返回。

     分析過程,有點亂,將就著看看吧,呵呵: 相信很多人都用到過datatables的點選按鈕,更新url,重新更換資料(前提兩次的資料結構是一樣的),所以需要使用到table.ajax.url(url).load()方法,table需要宣告成一個全域性變數,當點選一個按鈕,傳送一個新的action(新的url的)的時候,就需要呼叫這個方法ajax.url(url).load(),可惜你會在瀏覽器中發現url還是之前的url,雖然執行了方法,但是並沒有執行新的url,也就是沒有傳送新的action,如下圖debug可以發現:



    解決方法: 

    (1)所以接下來的最大的問題是如何能更換一個新的url,讓其重新執行新的url,找來找去stackflow網站,有很多人在提問這個問題,可沒找到答案,最後終於找到問題,連線如下:
    http://stackoverflow.com/questions/8667649/how-do-i-modify-ajax-url-of-a-query-datatable,這個連結的主要解決方式,既然url沒有更新,那就每次點選按鈕傳送新的action,

    手動去更新這個url,設定一下,千萬一點注意的是初始化datatables的方法應該用小寫的,大寫是沒有作用的,如下:

     wageNowTable = $('#sample_1').dataTable(datatables_options);

    wageNowTable = $('#sample_1').DataTable(datatables_options);使用大寫無法識別fnsettings(),驗證過,需要寫成小寫
    

    (2).不過還是遇到一個棘手的問題是:雖然已經更換了新的url,但介面並沒有重新整理資料,還是之前的介面,既然重新重新整理介面會恢復顯示的話,為了應付一下,只能再發送一次。而有時候卻是有用的,這個後面有大神可以講解一下:

//奇怪的是第一次重新繪製不會更新介面,我就笨一點的辦法再寫了一次,暫時的方法
wageNowTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
wageNowTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
     注意faDraw(false)裡的引數false,可以避免下面頁碼和顯示條數的更新,保持當前頁面的重新整理,如下解釋:

對於JqueryDataTable,在刪除非第一頁的資料後要重新整理資料卻會導致回到了第一頁很是憤怒。

解決辦法

例如你的table id 為tb,那麼使用下面的方法即可:

$("#tb").dataTable().fnDraw(false)

之所以加引數false是因為預設情況下fnDraw()是針對整個JqueryDataTable,而加了之後就是針對操作時Table時的列表

=====================華麗麗的分割線,這是最重要的我的解決方案===========================

    2. 重要部分(特別重要,以下是我在使用fnServerData,ajax請求資料的解決方案原始碼:):

     第一步是在jsp頁面,定義一個表格的全域性變數,直接放在一個<script>表格變數<script>,如下:

	<script type="text/javascript">
		
		var wageNowTable;//表格全域性變數,不要放在ready函式內,否則就不是全域性變數
		jQuery(document).ready(function() {
            });
       </script>
      第二步,在點選查詢的監聽函式裡面,首先判斷表格變數是否初始化,如果還沒初始化,就先初始化,配置引數。如果已經初始化,就使用替換url,重新重新整理表格的動作,如下:
$("#ceshi")
					.click(
							function() {
								var date = document.getElementById("wageDate").value;
								if (date.length == 0) {
									alert("請選擇月份");
									return;
								}
								
								
								var datatables_options = {
									"aLengthMenu" : [ [ 5, 10, 50, 100,500, -1 ],[ 5, 10, 50, 100,500, "All" ] ],
									"iDisplayLength" : 5,
									//如下的sdom,1.9的版本會顯示不出來,
									"sDom": 'T<"clear">rt<"float_left"i><"float_right_nexpage"p><"float_right_display"l>',
									//"sDom" : "<r>t<'float_left'i><'float_right_nexpage'p><'float_right_display'l>>",
									// 第一列禁止排序,因為這是複選框,不需要排序
									"bSort" : false,
									"bProcessing" : false,
									"bServerSide" : true,
									"bStateSave" : true, //是否開啟客戶端狀態記錄功能,此功能在ajax重新整理紀錄的時候不會將個性化設定回覆為初始化狀態  
									"bDestroy" : true,
									"bDeferRender":true,//延遲載入html元素
									"bJQueryUI" : false,
									"sScrollX" : "100%",
									"bStateSave":true,
									"language" : oLanguageData,
									// "aaData" : data,
									"aoColumns" : aoColumnsData,
									// 複選框點選下一頁進行更新狀態為未選中
									"fnDrawCallback" : function() {//關鍵點:需要先去掉checkbox的父級div的類checker,因為這個會影響選中的顯示效果
					                    $("#uniform-check-all").removeClass("checker");
					                    //切換下一頁,去掉原來全選的checkbox的狀態
					 					$("span").removeClass("checked");
					 					$("span #check-all").attr("checked",false);
					                   //更新表格
					                    $.uniform.update();
					                 },
									"fnRowCallback" : function(nRow, aData,
											iDataIndex) {
										var temp = aData.number;
										var temp = aData.name;
										$('td:eq(0)', nRow).html(
												"<input type='checkbox' name='everyline'  class='checkboxes' value="
														+ aData.number
														+ "></input>");
										//由於固定列的原因,當名字超過一定長度,表格就顯示錯亂,需要限定3個漢字,用...表示
										if (GetLength(aData.name) > 6) {
											$('td:eq(2)', nRow).html(
													"<a style='cursor:pointer' onclick='toWagePerson(\""
															+ date + "\",\""
															+ aData.identification
															+ "\");'>" + cutstr(aData.name, 6)
															+ "</a> ");
										}else{
											$('td:eq(2)', nRow).html(
													"<a style='cursor:pointer' onclick='toWagePerson(\""
															+ date + "\",\""
															+ aData.identification
															+ "\");'>" + aData.name
															+ "</a> ");
										}
										
										
									
									},
									"sAjaxSource" : "wageQuery.action?wageDate="
											+ date,
									"fnServerData" : function(sSource, aoData,
											fnCallback) {
										$.ajax({
											"type" : 'post',
											"async":false,
											"url" : sSource,
											"dataType" : "json",
											"data" : aoData,
											"success" : function(resp) {
												fnCallback(resp);
											}

										});
									},
									"fixedColumns":   {
								         "iLeftColumns" : 4,
									     "sHeightMatch" : "auto"
							        },

								};

								// 設定固定前三列的功能
								datatables_options["sScrollX"] = "100%";
								datatables_options["sScrollY"] = "400px";
								datatables_options["bScrollCollapse"] = true;
								// datatables_options["sScrollXInner"] = '150%';
								//判斷表格是否已經初始化。js中判斷物件是否為undefined,需要使用typeof的形式
								if (typeof(wageNowTable) == "undefined") {
									wageNowTable = $('#sample_1').dataTable(datatables_options);//初始化
									
									
								}else{
									var oSettings = wageNowTable.fnSettings();
									oSettings.sAjaxSource = "wageQuery.action?wageDate="
										+ date;
									wageNowTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
									wageNowTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
								}
								
								
								//	重要注意點:如果需要隱藏某些列,需要延遲執行才能繫結事件
								//	2016年10月31日16:38:51:qiulinhe:固定列的複選框在查詢資料之後更新狀態
									//重要一點:監聽複選框的狀態,重要需要使用on的形式,直接click無法改變狀態
								$('.DTFC_LeftHeadWrapper div').on('click','input' ,function () {
							                    $("#uniform-check-all").removeClass("checker");
							                    //切換下一頁,去掉原來全選的checkbox的狀態
							 					$("span").removeClass("checked");
												//表格繪製完成,監聽全選的複選框按鈕,將表格所有選中
							                            //alert("全選的按鈕事件");
										                var val = $(this).prop("checked");
										                $("input[type='checkbox']", ".DTFC_LeftHeadWrapper").attr("checked", val);
										                
										                $("#check-all").attr("checked", val);
										                if (val) {
										                	$(".checkboxes").each(function(index){
										                	     $(this).attr("checked", val);
										                	    // alert("設定下面全選");
										                	});
										                } else {//不勾選
											               $(".checkboxes").each(function(index){
										                	     $(this).attr("checked", val);
										                	     //alert("取消下面全選");
										                	});
										                }
							                      
							   } );		
					});

    附註: =================原文內容如下,給不能連線外網的同學看看:======================

    
    3.其實作者的解決方案是很簡單,就是拿到fnsettings所有設定的變數,更換裡面的sAjaxSource的url變數,重新繪製一遍就好,如下:
    主要是這句話: 
        var oTable = $("#example").dataTable({
              ...
       });
       var oSettings = oTable.fnSettings();
       oSettings.sAjaxSource = "some_url";//重新設定url
       //Nothing will happen
      oTable.Draw()
;
      

var oTable = $("#example").dataTable({
    ...
});


oTable.fnDraw();
should allow you to access and execute the functions for a specific table.


Information


Datatables doesn't support changing the settings after it's been initialized, and for a good reason.


//This does *NOT* work.
var oTable = $("#example").dataTable({
    ...
});
var oSettings = oTable.fnSettings();
oSettings.sAjaxSource = "some_url";
//Nothing will happen
oTable.Draw();
However, you could try using the fnServerData function to intercept the request before it is sent, and then just update the table with your own url whenever the change occur.
Or
You could destroy the table and reinitialize it.


To learn more about fnServerData, click here and search for "fnServerData".


Solution


I'm unsure whether or not this actually works, I haven't done it before, but it should work.


var currentSource = "this_url";
var oTable = $('#example').dataTable( {
    "bServerSide": true,
    "bProcessing": true,
    "aoColumns": [
        {"sTitle": "id"},
        {"sTitle": "name"}
    ],
    "sPaginationType": "full_numbers",
    "sAjaxSource": currentSource,
    "fnServerData": function ( sSource, aoData, fnCallback ) {
        $.ajax( {
            "dataType": 'json',
            "type": "POST",
            "url": currentSource,
            "data": aoData,
            "success": fnCallback
        });
    }
});


$("#SelectedId").change(function)(){
    currentSource = "new_url";
    oTable.fnDraw(); //or fnReloadAjax()
});
Alternative Solution


An alternative would be to destroy the table and then reinitialize it with the new settings. This is, however, a very ineffective way of handling it.


var initParams = {
    "bServerSide": true,
    "bProcessing": true,
    "aoColumns": [
        {"sTitle": "id"},
        {"sTitle": "name"}
    ],
    "sPaginationType": "full_numbers",
    "sAjaxSource": "this_url",
};
var oTable = $('#example').dataTable(initParams);


$("#SelectedId").change(function)(){
    oTable.fnDestroy();
    initParams.sAjaxSource = "new_url";
    oTable = $('#example').dataTable(initParams);
});
Sidenote


When you've got bServerSide = true you have to take care of everything, and that means it complicates everything too!


Happy coding! :)




  ========================================================以下是找到的資料和網站收集,讓別人不用在浪費時間搜尋了============================


      4.第一次dataTable為null,則建立,後面則只是更換url,重新載入資料,可以發現在fnServerData裡面取到的總是最開始載入的url,後面更新的url沒有取到。fnServerData 就是替換預設的ajax的載入過程,在ajax執行前加入一些自己的程式碼,最後把這個函式用fnPreDrawCallback方法替換,fnPreDrawCallback也是在生成表格前執行,也可以達到我的目的,這樣就可以把fnServerData去掉,使用預設的ajax過程,這樣修改後發現每次都能載入到最新的url,不知道這是不是dataTable的bug,還是我哪裡沒有配置好。
    總結一下:使用了fnServerData, 導致ajax.url(url).load()沒有載入最新的url的資料,而是一直使用的最開始的那個url. 
    參照官方網站的切換url,重新發送ajax請求:http://datatables.club/reference/api/ajax.url().load().html,http://datatables.club/reference/api/ajax.url().html,
    
    有同樣問題的網址,都沒解決:https://datatables.net/forums/discussion/23911/cant-reload-datatable-from-new-source-used-both-fnreloadajax-and-ajax-url-load
    http://stackoverflow.com/questions/27787462/datatables-1-10-reload-ajax-data
    https://datatables.net/forums/discussion/22701/reload-table-with-ajax-url-load-not-working

    http://stackoverflow.com/questions/29110251/datatables-ajax-reload-from-new-source,

      https://my.oschina.net/crazyharry/blog/356337

    http://stackoverflow.com/questions/8667649/how-do-i-modify-ajax-url-of-a-query-datatable(這裡是最終的解決方案連結)

     5.所以上述的過程的思想是更換url,使用新的ajax的action,取得後臺資料,然後使用fndraw(false)重新整理介面。為了避免每次都點選按鈕新建表格,可以複用這個表格的初始化動作,這樣就不會導致表格每次點選查詢,變形再恢復的bug。

    6.這是在stack overflow上新增的回答,裡面好多中文希望有老外看得懂,哈哈:http://stackoverflow.com/questions/27787462/datatables-1-10-reload-ajax-data/40907779#40907779

=======================================分割線==========================================

    1.可能是自己使用的理解上還有問題,發現只使用inforCheckTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數,一遍也可以實現重新整理表格的功能,不過後期遇到這樣一個問題,當使用了後臺分頁,有一個多條件查詢的頁面,每次點選需要拼接查詢,查詢出結果出來之後,當選中了第3頁開始,然後又要重新點選查詢,你會發現命名有資料,卻報錯,這是因為第3頁的時候,傳給後臺是第三頁的資訊,而第三頁沒有,所以需要重新設定從第一頁開始,類似於這樣:

//判斷表格是否已經例項化,沒有則例項化,已經例項化,則直接更新,載入對應的url
	if (typeof(inforCheckTable) == "undefined") {
		inforCheckTable = $('#serverside_inforTable') .on( 'processing.dt', function ( e, settings, processing ) {
			$('#serverside_inforTable_processing').css( 'color', processing ? 'red' : 'red' );//設定處理中為紅色
			$('#serverside_inforTable_processing').css( 'display', processing ? 'block' : 'none' );
	        
	    } ).dataTable(datatables_options);
		
	}else{
		var oSettings = inforCheckTable.fnSettings();
		oSettings.sAjaxSource = "",
		oSettings._iDisplayStart = 0;
		inforCheckTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
		//inforCheckTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
	}

     2.當datatables表格獲取資料,使用ajax的時候,如果你返回的資料是為null的,就會導致success返回回來報錯,表格卡住不動,你需要在後臺處理的時候,如果資料為空,你就新建一個new一個物件,這樣就可以避免報錯了。

   3.有時候會莫名其妙遇到使用ajax卡住的問題,後來才發現是因為後臺分頁的時候,想當然的在後臺分頁的資料結構裡將sEcho設為0,更改了sEcho屬性,把它設為0就解決了,還有就是在重新發送url的時候,重新設定iDraw的屬性

	//判斷表格是否已經例項化,沒有則例項化,已經例項化,則直接更新,載入對應的url
				if (typeof(wagePreviewTable) == "undefined") {
					wagePreviewTable = $('#sample_1').dataTable(datatables_options);
					
				}else{
					var oSettings = wagePreviewTable.fnSettings();
					oSettings.iDraw="3";//當重新發送ajax,如果遇到卡在那裡,可以加這個屬性
					oSettings.sAjaxSource = "wagePreviewGenerate.action?listInforSelectSpilt=" + listInforSelectSpilt;
					
					wagePreviewTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
					wagePreviewTable.fnDraw(false);//不會跳轉到第一頁,保留頁面的頁碼和顯示條數
				}
					
            就是這個iDraw屬性,參考官方說明:
http://jakzaprogramowac.pl/pytanie/32885,datatables-processing-message-stuck-on-grid-reload
http://stackoverflow.com/questions/37444620/datatables-processing-message-stuck-on-grid-reload
You have server-side processing enabled with serverSide: true. In that mode server should return draw parameter in the response containing the same value as in the request. The value starts at 1 and increments with every request.

Most likely you're returning the same value in draw parameter and that's why your subsequent responses are ignored.

See server-side processing documentation for more details.

=======================================分割線,分頁工具類和使用方法=======================

   注:   附上分頁資訊工具類:

package rms.filter.dataFilter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import rms.data.DatatableSourceStruct;
import rms.data.PersonWage;
import rms.filter.dataFilter.impl.DataFilterImpl;
import rms.util.LogUtil;

/**
 * 分頁工具類
 * 
 * @author zhanglei
 * 
 */
public class WageDataFilter implements DataFilterImpl {

	private List<PersonWage> wages;
	private String methodNames;

	public WageDataFilter(List<PersonWage> wages) {//替換成自己的list資料來源
		this.wages = wages;
		this.initMethodNames();
	}

	@Override
	public DatatableSourceStruct getDataSource(int totalNum, int length,
			String sortString, String searchString, List<String> searchPropList) {
		DatatableSourceStruct dataSource = new DatatableSourceStruct();
		if (wages != null && wages.size() > 0) {
			List<Object> tempList = new ArrayList<Object>();
			if (length == -1) {
				tempList.addAll(wages);
			} else {
				// this.filteArray(searchString, searchPropList);
				// this.sortArray(sortString);

				for (int i = 0; i < wages.size(); i++) {
					tempList.add(wages.get(i));
				}
			}
			// dataSource.setDraw(3);
			dataSource.setAaData(tempList);
			dataSource.setiTotalDisplayRecords(totalNum);
			dataSource.setiTotalRecords(totalNum);
			// dataSource.setiTotalRecords(totalNum);
		} else {
			// dataSource.setDraw(3);
			dataSource.setAaData(new ArrayList<Object>());
			dataSource.setiTotalDisplayRecords(0);
			dataSource.setiTotalRecords(0);
		}

		return dataSource;
	}

	@Override
	public DatatableSourceStruct getDataSource(int totalNum, int length,
			String searchString, List<String> searchPropList) {
		return getDataSource(totalNum, length, "", searchString, searchPropList);
	}

	@Override
	public DatatableSourceStruct getDataSource(int totalNum, int length,
			String sortString) {
		return getDataSource(totalNum, length, sortString, "",
				new ArrayList<String>());
	}

	@Override
	public DatatableSourceStruct getDataSource(int totalNum, int length) {
		return getDataSource(totalNum, length, "", null);
	}

	/**
	 * 注意:取出的的資料,如果按照編號來排序的話,與狼工資有可能為空,需要加判斷,以身份證號為排序
	 * 
	 * @param sor
	 */
	@SuppressWarnings("unused")
	private void sortArray(String sor) {
		// 當月工資和歷史工資裡儲存的都是已接收狀態的軍休幹部的工資資訊,所以根據軍休幹部資訊的編號進行排序
		Collections.sort(wages, new Comparator<PersonWage>() {
			@Override
			public int compare(PersonWage o1, PersonWage o2) {
				if (o1.getNumber() == null || o2.getNumber() == null) {
					// 預覽工資中編號有可能為空,要以身份證號為排序標準
					return o1.getIdentification().compareTo(
							o2.getIdentification());
				}

				return o1.getNumber().compareTo(o2.getNumber());
			}
		});
	}

	@SuppressWarnings("unused")
	private void filteArray(String searchString, List<String> searchPropList) {
		if (searchString == null || searchString.isEmpty()) {
			return;
		}

		ArrayList<String> propList = new ArrayList<String>();
		for (String prop : searchPropList) {
			if (this.hasProp(prop)) {
				propList.add(prop);
			}
		}

		ArrayList<PersonWage> personWages = new ArrayList<PersonWage>();

		for (PersonWage pw : wages) {
			boolean isContain = false;
			for (String prop : propList) {
				if (containsSearchString(pw, searchString, prop)) {
					isContain = true;
					break;
				}
			}
			if (isContain) {
				personWages.add(pw);
			}
		}
		wages = personWages;
	}

	private boolean containsSearchString(PersonWage personWage,
			String searchString, String prop) {
		try {
			Method method = PersonWage.class.getMethod(prop, new Class[] {});
			String result = (String) method.invoke(personWage, new Object[] {});
			if (result == null) {
				return false;
			}
			if (result.contains(searchString)) {
				return true;
			}
		} catch (SecurityException e) {
			LogUtil.error(e);
			return false;
		} catch (NoSuchMethodException e) {
			LogUtil.error(e);
			return false;
		} catch (IllegalArgumentException e) {
			LogUtil.error(e);
			return false;
		} catch (IllegalAccessException e) {
			LogUtil.error(e);
			return false;
		} catch (InvocationTargetException e) {
			LogUtil.error(e);
			return false;
		}

		return false;
	}

	private boolean hasProp(String methodName) {
		return this.methodNames.contains(methodName);
	}

	private void initMethodNames() {
		this.methodNames = "";
		for (Method med : PersonWage.class.getMethods()) {
			methodNames += med.getName() + ",";
		}
	}

}


           使用方式步驟如下:

1.//action裡面的程式碼
public String dataQuery() {
try {
Map<String, Object> session = ActionContext.getContext()
.getSession();


setCeshi(retireWageMonthInfoManager.wageDataQuery(//傳入分頁資訊,從資料庫裡面查詢分頁的資料,然後進行分頁處理,返回DatatableSourceStruct
RetireInforStatus.RECEIPTED.getCode(), wageDate, userID,
ConstantUtil.WAGE_TYPE_NOW, start, pageSize, sEcho));

} catch (Exception e) {
LogUtil.error(e);
}
return SUCCESS;
}

2.//業務層的處理
@Override
@Transactional(readOnly = true)
public DatatableSourceStruct wageDataQuery(Integer status, String wageDate,
int userID, int wageType, int start, int pageSize, int sEcho)
throws Exception {
int totalNum = inforDao.getRetireinforsNumberByStatus(status);
List<PersonWage> personWageList = retireWageMonthInfoDAO
.getPersonWageListByPage(wageDate, userID, wageType, start,
pageSize);
return filteWageData(totalNum, pageSize, sEcho, personWageList, null);
}

3.//在strut2配置檔案返回給ajax的success資料json物件
private DatatableSourceStruct filteWageData(int totalNum, int pageSize,
int sEcho, List<PersonWage> wages, String sSearch) {
ArrayList<String> searchPropList = new ArrayList<String>();
// 暫時 預設的 退休證號碼 、編號 和 name,用來定義搜尋內容框
searchPropList.add("getNumber");
searchPropList.add("getName");
searchPropList.add("getRetirementCardNo");


WageDataFilter wageHisDataFilter = new WageDataFilter(wages);
wageDatatableSource = wageHisDataFilter.getDataSource(totalNum,
pageSize, sSearch, searchPropList);
wageDatatableSource.setsEcho(0);
return wageDatatableSource;
}

4.strutc2.xml裡面配置返回的json物件,使用者datatables裡面使用

<action name="wageNowGenerate" class="wage" method="generateWageData">
<result name="success" type="json">
<param name="root">wageDatatableSource</param>
</result>
</action>

      經過這四個步驟就可以實現後臺分頁了。抱歉寫的太亂了,本來想上傳原始碼的,不過是公司的程式碼,所以只能貼部分了,遇到問題可以找我,畢竟遇到了很多坑。