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' οnclick='toWagePerson(\""
															+ date + "\",\""
															+ aData.identification
															+ "\");'>" + cutstr(aData.name, 6)
															+ "</a> ");
										}else{
											$('td:eq(2)', nRow).html(
													"<a style='cursor:pointer' οnclick='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>

      经过这四个步骤就可以实现后台分页了。抱歉写的太乱了,本来想上传源码的,不过是公司的代码,所以只能贴部分了,遇到问题可以找我,毕竟遇到了很多坑。

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值