【发布时间】:2012-05-14 17:31:57
【问题描述】:
在网格中显示实时数据(证券交易所、天气等)的更好方法是什么?
我用这个方法:
setInterval(function(){
jQuery("#list1").trigger('reloadGrid');
}, 5000);
【问题讨论】:
标签: jqgrid
在网格中显示实时数据(证券交易所、天气等)的更好方法是什么?
我用这个方法:
setInterval(function(){
jQuery("#list1").trigger('reloadGrid');
}, 5000);
【问题讨论】:
标签: jqgrid
我觉得你的问题很有趣。我认为这个问题对于许多其他用户来说可能很有趣。所以我要 +1。
setInterval 的使用使我在常见的独立于浏览器的情况下最好的方式。应该将setInterval 的结果保存在一个变量中,以便能够使用clearInterval 来停止它。
另一个小的改进是使用[{current:true}](详见the answer)作为trigger的第二个参数:
var $grid = jQuery("#list1"), timer;
timer = setInterval(function () {
$grid.trigger('reloadGrid', [{current: true}]);
}, 5000);
它会在重新加载网格时保存选择。
许多新的网络浏览器现在支持WebSocket。因此,如果网络浏览器支持它,最好使用这种方式。这样一来,如果服务器上的数据未更改并防止服务器永久池化,则可以跳过不必要的网格重新加载。
一种更常见的方式在我看来也很有趣。如果可以使用网格数据最后更改的某种时间戳,则可以验证数据是否已更改。为此,可以使用ETag 或服务器上的一些通用附加方法。
填充jqGrid的jQuery.ajax的当前success回调允许您使用beforeProcessing回调来修改服务器响应,但它不允许您停止jqGrid刷新 基于jqXHR.satus 值(在当前jqGrid 代码中为xhr.status)。对 jqGrid 代码的the line 进行小修改将允许您在服务器上的数据未更改的情况下停止 jqGrid 刷新。可以为"notmodified" 测试textStatus(在当前jqGrid 代码中为st)或为304 测试jqXHR.satus(在当前jqGrid 代码中为xhr.status)。重要的是要使用该场景,您应该使用prmNames: { nd:null } 选项并设置ETag 和Cache-Control: private, max-age=0(有关更多信息,请参阅here、here 和here)。
更新:我尝试根据我最后的建议创建演示项目,发现它不像我上面描述的那么容易。尽管如此,我还是让它工作了。困难在于无法从 jQuery.ajax 内部的服务器响应中看到 304 代码。原因是XMLHttpRequest specification中的以下地方
对于用户代理导致的 304 Not Modified 响应 生成的条件请求用户代理必须像服务器一样操作 给出了带有适当内容的 200 OK 响应。用户代理 必须允许作者请求标头覆盖自动缓存 验证(例如 If-None-Match 或 If-Modified-Since),在这种情况下 304 Not Modified响应必须通过。
所以人们在$.ajax 的success 处理程序中看到200 OK 状态,而不是来自服务器响应的304 Not Modified。似乎XMLHttpRequest 从缓存中获取完整响应,包括所有 HTTP 标头。所以我决定改变对缓存数据的分析,只是将上次 HTTP 响应中的 ETag 保存为新的 jqGrid 参数,并用保存的数据测试新响应的 ETag。
我看到你使用 PHP(我不使用 :-()。不过我可以阅读和理解 PHP 代码。我希望你能以同样的方式阅读 C# 代码并理解主要思想。所以你会能够在 PHP 中实现相同的功能。
现在我描述我做了什么。首先我修改了jqGrid源代码的the lines,它使用了来自
的beforeProcessing回调
if ($.isFunction(ts.p.beforeProcessing)) {
ts.p.beforeProcessing.call(ts, data, st, xhr);
}
到
if ($.isFunction(ts.p.beforeProcessing)) {
if (ts.p.beforeProcessing.call(ts, data, st, xhr) === false) {
endReq();
return;
}
}
它将允许从 beforeProcessing 返回 false 以跳过数据刷新 - 跳过数据处理。我在demo中使用的beforeProcessing的实现是基于ETag的用法:
beforeProcessing: function (data, status, jqXHR) {
var currentETag = jqXHR.getResponseHeader("ETag"), $this = $(this),
eTagOfGridData = $this.jqGrid('getGridParam', "eTagOfGridData");
if (currentETag === eTagOfGridData) {
$("#isProcessed").text("Processing skipped!!!");
return false;
}
$this.jqGrid('setGridParam', { eTagOfGridData: currentETag });
$("#isProcessed").text("Processed");
}
$("#isProcessed").text("Processed"); 行或$("#isProcessed").text("Processing skipped!!!"); 行在 div 中设置了 "Processed" 或 "Processing skipped!!!" 文本,我用这些文本直观地表明来自服务器的数据用于填充网格。
在演示中我显示了两个具有相同数据的网格。我用来编辑数据的第一个网格。第二个网格每秒从服务器中提取数据。如果服务器上的数据未更改,则 HTTP 流量如下所示
HTTP 请求:
GET http://localhost:34336/Home/DynamicGridData?search=false&rows=10&page=1&sidx=Id&sord=desc&filters= HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: application/json, text/javascript, */*; q=0.01
Referer: http://localhost:34336/
Accept-Language: de-DE
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; LEN2)
Host: localhost:34336
If-None-Match: D5k+rkf3T7SDQl8b4/Y1aQ==
Connection: Keep-Alive
HTTP 响应:
HTTP/1.1 304 Not Modified
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 06 May 2012 19:44:36 GMT
X-AspNet-Version: 4.0.30319
X-AspNetMvc-Version: 2.0
Cache-Control: private, max-age=0
ETag: D5k+rkf3T7SDQl8b4/Y1aQ==
Connection: Close
因此,如果数据未更改,不会从服务器传输任何数据。如果数据发生变化,HTTP 标头将以HTTP/1.1 200 OK 开头,包含新修改数据的ETag 以及数据页本身。
可以使用导航器的“刷新”按钮手动刷新网格数据,也可以使用每秒执行$grid1.trigger('reloadGrid', [{ current: true}]); 的“开始自动刷新”按钮刷新网格数据。页面看起来像
我在页面底部用颜色框标记了最重要的部分。如果有一个loadui: "disable" 选项,那么在拉动服务器期间根本看不到网格上的任何变化。如果有人评论了一个选项,即会在很短的时间内看到“正在加载...” div,但 没有网格包含会闪烁。
启动“自动刷新”后,您会看到如下图所示
如果要更改第一个网格中的某些行,第二个网格将在一秒钟内更改,并且将看到 "Processed" 文本,然后再更改为 "Processing skipped!!!" 文本。
服务端对应的代码(我用的是ASP.NET MVC)大多如下
public JsonResult DynamicGridData(string sidx, string sord, int page, int rows,
bool search, string filters)
{
Response.Cache.SetCacheability (HttpCacheability.ServerAndPrivate);
Response.Cache.SetMaxAge (new TimeSpan (0));
var serializer = new JavaScriptSerializer();
... - do all the work and fill object var result with the data
// calculate MD5 from the returned data and use it as ETag
var str = serializer.Serialize (result);
byte[] inputBytes = Encoding.ASCII.GetBytes(str);
byte[] hash = MD5.Create().ComputeHash(inputBytes);
string newETag = Convert.ToBase64String (hash);
Response.Cache.SetETag (newETag);
// compare ETag of the data which already has the client with ETag of response
string incomingEtag = Request.Headers["If-None-Match"];
if (String.Compare (incomingEtag, newETag, StringComparison.Ordinal) == 0) {
// we don't need return the data which the client already have
Response.SuppressContent = true;
Response.StatusCode = (int)HttpStatusCode.NotModified;
return null;
}
return Json (result, JsonRequestBehavior.AllowGet);
}
我希望不仅使用 ASP.NET MVC 的人也能清楚代码的主要思想。
您可以下载项目here。
更新:我发布了the feature request 以允许beforeProcessing 通过返回false 值来中断服务器响应的处理。相应的更改已经包含在主 jqGrid 代码中(参见here)。所以 jqGrid 的下一个版本将包含它。
【讨论】:
gridview: true 选项。因此,通过将innerHTML 设置为表格主体,将使用一个操作更新整个网格主体。如果您要单独修改jqGrid的许多行甚至单元格,则重新加载整个网格可能会很慢。此外,如果添加或删除了某些行,则也必须修改寻呼机。所以重新加载网格似乎是我最好的方法。
$grid.trigger('reloadGrid', [{current: true}]) 重新加载整个网格体。问题只是代码究竟做了什么,datatype 有网格、网格的页面大小以及您的确切场景的许多其他部分。答案很古老,jqGrid 现在有很多新功能。无论如何,如果您确定只更改了一行并且您获得了该行的数据,则可以使用setRowData 方法。另一方面,网格中一行的变化可以跟随页面上其他行和其他 HTML 元素的位置的变化。这样网络浏览器重排就完成了。