使用 AJAX (Ext JS)、JSON (Jayrock) 和 LINQ 进行滚动数据分页






4.71/5 (13投票s)
结合滚动条事件、JSON RPC 调用和 LINQ,创建流畅、快速的“无需点击”分页 DataGrid。
引言
厌倦了不断点击下一页、下一页、下一页来查找您想要的数据?我一直以为这是在互联网上查看数据时必须面对的问题,直到我看到了新的 Microsoft live 图像搜索。它没有强制用户点击下一页来分页数据,而是捕获滚动事件,异步获取下一页数据并将其添加到输出中。在此项目中,我将扩展我之前的项目 使用 LINQ 分页您的 ObjectDataSource,将标准网格替换为滚动分页网格。这个 示例 的工作方式是,首先创建一个 Ext JS 网格,然后调用自定义 JSON RPC 处理程序 来检索数据,并通过为网格的滚动事件添加事件侦听器来实现。本文将主要关注此项目的 AJAX 部分,有关底层数据分页技术的更多信息,请参阅 之前的版本。
本项目涵盖的主题
- 单例集合
- 使用 LINQ 查询对象
- 在 LINQ 中使用
Skip()
和Take()
- 自定义数据分页
- 使用 Jayrock 创建自定义 JSON RPC 处理程序
- 在 ASP.NET 和 JSON RPC 中使用 Ext JS
- 处理 JavaScript 事件和异步 JavaScript
背景
本项目是使用 LINQ 查询 Singleton 集合的一个示例。这基本上意味着集合在您的 IIS 进程的应用程序级别持久化在内存中。这是通过使用静态或共享实例,或使用 .NET Web 缓存,或其他第三方缓存机制(如 zcache 或 ncache)来实现的。如果您不想将数据持久化在内存中,那么 rownumber 是您实现数据分页的另一种方式。您可以将 JSON RPC 处理程序访问以检索其数据类的引用替换为直接从数据库加载的数据库查询或对象。
使用代码
下载的 Zip 文件包含 Visual Studio 2008 项目和运行 在线演示 所需的源代码文件,以及一个包含邮政编码数据库的 CSV 文件和一个用于加载代码中引用的数据的存储过程。在导入邮政编码数据并运行存储过程脚本后,您需要更新 web.config 文件中的连接字符串以指向您的数据源。此外,此 Zip 文件还包含运行此项目所需的 Ext JS 库和 Jayrock DLL。
关注点
在开始之前,您需要一种机制来在用户滚动数据列表时异步检索数据。我选择使用通用处理程序来返回 JSON 序列化字符串以最大化性能和吞吐量。
C#
using System;
using System.Web;
using Jayrock.Json;
using Jayrock.JsonRpc;
using Jayrock.JsonRpc.Web;
using ZipCodeObjects;
using System.Collections.Generic;
/// <summary>
/// GetZips
/// </summary>
/// <remarks>
/// Json Handler for zip code information.
/// </remarks>
public class GetZipCodes : JsonRpcHandler
{
/// <summary>
/// GetZipCodesCount
/// </summary>
/// <returns></returns>
/// <remarks>
/// get count of all zip codes.
/// </remarks>
[JsonRpcMethod("GetZipCodesCount")]
public int GetZipCodesCount()
{
return ZipCodeCollection.SelectCount();
}
/// <summary>
/// GetZipCodes
/// </summary>
/// <param name="ResultsPerPage"></param>
/// <param name="PageNumber"></param>
/// <returns></returns>
/// <remarks>
/// Get paginated zip codes data.
/// </remarks>
[JsonRpcMethod("GetZipCodesList")]
public IEnumerable<ZipCode> GetZipCodesList(int ResultsPerPage, int PageNumber)
{
return ZipCodeCollection.GetZipCodes(ResultsPerPage, PageNumber);
}
}
VB.NET
Imports System
Imports System.Web
Imports Jayrock.Json
Imports Jayrock.JsonRpc
Imports Jayrock.JsonRpc.Web
Imports LinqListGridBind.ZipCodeObjects
''' <summary>
''' GetZips
''' </summary>
''' <remarks>
''' Json Handler for zip code information.
''' </remarks>
Public Class GetZipCodes
Inherits JsonRpcHandler
''' <summary>
''' GetZipCodesCount
''' </summary>
''' <returns></returns>
''' <remarks>
''' get count of all zip codes.
''' </remarks>
<JsonRpcMethod("GetZipCodesCount")> _
Public Function GetZipCodesCount() As Integer
Return ZipCodeCollection.SelectCount()
End Function
''' <summary>
''' GetZipCodes
''' </summary>
''' <param name="ResultsPerPage"></param>
''' <param name="PageNumber"></param>
''' <returns></returns>
''' <remarks>
''' Get paginated zip codes data.
''' </remarks>
<JsonRpcMethod("GetZipCodesList")> _
Public Function GetZipCodesList(ByVal ResultsPerPage As Integer, _
ByVal PageNumber As Integer) _
As IEnumerable(Of ZipCode)
Return ZipCodeCollection.GetZipCodes(ResultsPerPage, PageNumber)
End Function
End Class
此示例不使用回发、视图状态或会话状态。数据存储在 DOM 中并通过 JavaScript 访问。
//create ext data store and grid.
var store = null;
var grid = null;
//set default scroll variables.
var currentpos = 0;
var currentpage = 1;
//set constants.
//itemsperpage: number of records that appear on each page of content.
var itemsperpage = 14;
//number of pages of content to fetch when offset reached.
var pagestofetch = 2;
//number of pixel interval to fire handler request.
var scrolloffset = 263;
var scrolloffsetinterval = 263;
//event fires when ext is loaded and ready to display controls.
Ext.onReady(function(){
//set current count.
var x = document.getElementById("currentcount");
x.innerHTML = (itemsperpage*pagestofetch);
//create reference to GetZipCodes.ashx
var s = new GetZipCodes();
//call GetZipCodes.ashx handler
//to retrieve total records in collection.
s.GetZipCodesCount(function(response) {
var x = document.getElementById("totalcount");
x.innerHTML = response.result;
});
//set skip take values.
var y = document.getElementById("skip1");
y.innerHTML = '0';
var z = document.getElementById("skip2");
z.innerHTML = '0';
var a = document.getElementById("take1");
a.innerHTML = (itemsperpage * pagestofetch);
var b = document.getElementById("take2");
b.innerHTML = (itemsperpage * pagestofetch);
//set ext state provider.
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
//initialize temp data store for handler response.
var myData
//call GetZipCodes.ashx to retrieve zip code data.
s.GetZipCodesList(
itemsperpage*pagestofetch,
currentpage,
function(response) { //async callback fires when handler returns data.
//store response.
myData = response;
//create jason data store and load data.
store = new Ext.data.JsonStore({data: myData,
root: 'result',
fields: ['zip',
'city',
'state',
'latitude',
'longitude',
'timeZone',
'dst']
});
//create the ext Grid and load data store
grid = new Ext.grid.GridPanel({
store: store,
columns: [
{header: "Zip", width: 120, sortable: false, dataIndex: 'zip'},
{header: "City", width: 120, sortable: false, dataIndex: 'city'},
{header: "State", width: 120, sortable: false, dataIndex: 'state'},
{header: "Latitude", width: 120, sortable: false, dataIndex: 'latitude'},
{header: "Longitude", width: 120, sortable: false, dataIndex: 'longitude'},
{header: "Time Zone", width: 120, sortable: false, dataIndex: 'timeZone'},
{header: "DST", width: 120, sortable: false, dataIndex: 'dst'}
],
stripeRows: true,
height:350,
autoExpandColumn:6,
header: false,
title:'Zip Code Listing'
});
//render grid
grid.render('zip-grid');
//add scroll event listener
grid.addListener('bodyscroll',scrollListener);
}); //end Json call listener.
}); //end ext ready listener
//scrollListener
//fires when grid is scrolled.
function scrollListener(scrollLeft, scrollTop){
//only handle scroll downs past highest position.
if ( scrollTop > currentpos )
{
//check if we should get more data
if ( scrollTop > scrolloffset )
{
//store current grid scroll state.
var state = grid.getView().getScrollState();
//adjust scroll offset
scrolloffset=scrollTop+scrolloffsetinterval;
//adjust current page
currentpage=currentpage+1;
//initialize temp data store for handler response.
var myData
//call GetZipCodes.ashx to retrieve zip code data for next pages of data
var s = new GetZipCodes();
s.GetZipCodesList(
itemsperpage*pagestofetch,
currentpage,
function(response) { //async callback fires when handler returns data.
//store response.
myData = response;
//append items to json store.
store.loadData(myData,true);
//restore grid scroll state.
grid.getView().restoreScroll(state);
//set current count.
var x = document.getElementById("currentcount");
x.innerHTML = store.getCount();
//set skip take values.
var y = document.getElementById("skip1");
y.innerHTML = (((currentpage-1) * pagestofetch) * itemsperpage);
var z = document.getElementById("skip2");
z.innerHTML = (((currentpage-1) * pagestofetch) * itemsperpage);
var a = document.getElementById("take1");
a.innerHTML = (itemsperpage * pagestofetch);
var b = document.getElementById("take2");
b.innerHTML = (itemsperpage * pagestofetch);
}); //end: async callback fires when handler returns data.
} //end: check if we should get more data
//reset current scroll position
currentpos=scrollTop;
} //end last position test.
} //end: scrollListener
下载演示项目以获取完整的源代码和数据库。要在线查看 LINQ 和基类代码,请参阅 使用 LINQ 分页您的 ObjectDataSource。
历史
- 2008/4/03:发布代码示例和文章。
- 2008/4/03:上传示例项目。