ExtJs学习笔记(22)-XTemplate + WCF 打造无刷新数据分页

ExtJs的Grid组件虽然不管从哪一方面来讲,都称得上是很好很强大,但是总会有一些应用场景并不需要这么多功能,比如网站的留言列表,开发者只想要一个简单的<li>或<table>列表而已,这时候XTemplate就显得很有用了。 本文将讲解如何用XTemplate结合WCF与服务端交互,生成数据列表,同时加上无刷新分页功能(默认情况下ExtJs并没有为XTemplate并没有提供分页功能) 1.先做一些准备工作,写一个通用的类(改编自老张的PageData),用于WCF向ExtJs返回分页数据

Code
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

/// <summary>
///PageData 的摘要说明
/// </summary>
/// 
[DataContract]
public class PageData<T>
{
    [DataMember]
    public int RecordCount { set; get; }

    [DataMember]
    public int PageSize { set; get; }

    [DataMember]
    public int PageCount { set; get; }

    [DataMember]
    public int CurrentPageIndex { set; get; }

    [DataMember]
    public T Data { set; get; }
}

2.DateTime序列化问题,因为.net序列化DateTime时,不管你怎么努力,只要是DateTime类型,最终只能生成类似 "F_Date":"\/Date(1221023588109+0800)\/"这样的字符串,ExtJs并不能正确识别!为此我们需要一个第三方的用于序列化DateTime的小工具Newtonsoft.Json.dll,它是专门用于将对象序列化成Json字符串。重要的是,用这个序列化后的DateTime字符串,ExtJs能够识别(注:百度搜索一下"Newtonsoft.Json"很容易就能找到N多下载的,下载后直接添加到项目引用里即可) 3.编写具体的实体类T_GuestBook,直接在数据库里建好,拖到dbml里就可以了,主要代码如下(注意要设置dbml的序列化属性为"单向",否则vs不会自动为class以及成员加上序列化标签):

Code
[Table(Name="dbo.T_GuestBook")]
[DataContract()]
public partial class T_GuestBook : INotifyPropertyChanging, INotifyPropertyChanged
{
    
    
    
    [Column(Storage="_F_ID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
    [DataMember(Order=1)]
    public int F_ID
    {
        
    }
    
    [Column(Storage="_F_IP", DbType="NVarChar(15) NOT NULL", CanBeNull=false)]
    [DataMember(Order=2)]
    public string F_IP
    {
        
    }
    
    [Column(Storage="_F_Date", DbType="DateTime NOT NULL")]
    [DataMember(Order=3)]
    public System.DateTime F_Date
    {
        
    }
    
    [Column(Storage="_F_Content", DbType="NVarChar(1000) NOT NULL", CanBeNull=false)]
    [DataMember(Order=4)]
    public string F_Content
    {
        
    }
    
    [Column(Storage="_F_Reply", DbType="NVarChar(1000) NOT NULL", CanBeNull=false)]
    [DataMember(Order=5)]
    public string F_Reply
    {
        
    }
    
    
}

为阅读方便,去掉了一些自动生成的代码

4.建一个WCF服务,并添加一个方法:

Code
[OperationContract]
    [WebInvoke(ResponseFormat = WebMessageFormat.Json,UriTemplate = "GetData2?PageSize={PageSize}&PageIndex={PageIndex}", Method = "*")]
    public string GetData2(int PageSize, int PageIndex)
    {
        System.Threading.Thread.Sleep(1000);//为演示Ajax加载效果,停1秒

        List<T_GuestBook> _List = new List<T_GuestBook>();
        _List.Add(new T_GuestBook() { F_ID = 1, F_IP = "192.23.37.41", F_Date = DateTime.Now, F_Content = "这是第一条留言", F_Reply = "" });
        _List.Add(new T_GuestBook() { F_ID = 2, F_IP = "192.168.0.1", F_Date = DateTime.Now, F_Content = "这是第二条留言", F_Reply = "" });
        _List.Add(new T_GuestBook() { F_ID = 3, F_IP = "192.168.0.2", F_Date = DateTime.Now, F_Content = "这是第三条留言", F_Reply = "" });
        _List.Add(new T_GuestBook() { F_ID = 4, F_IP = "172.168.235.1", F_Date = DateTime.Now, F_Content = "这是第四条留言", F_Reply = "" });
        _List.Add(new T_GuestBook() { F_ID = 5, F_IP = "10.200.30.4", F_Date = DateTime.Now, F_Content = "这是第五条留言", F_Reply = "" });
        _List.Add(new T_GuestBook() { F_ID = 6, F_IP = "10.200.30.5", F_Date = DateTime.Now, F_Content = "这是第六条留言", F_Reply = "" });
        //以上操作也可以改为利用Linq直接从数据库读取



        PageData<List<T_GuestBook>> _PageData = new PageData<List<T_GuestBook>>();

        _PageData.RecordCount = _List.Count;

        if (PageSize <= 0) { PageSize = 1; }
        if (PageSize > _PageData.RecordCount) { PageSize = _PageData.RecordCount; }
        _PageData.PageSize = PageSize;

        //计算总页数
        if (_PageData.RecordCount % _PageData.PageSize == 0)
        {
            _PageData.PageCount = (_PageData.RecordCount / _PageData.PageSize);
        }
        else
        {
            _PageData.PageCount = (_PageData.RecordCount / _PageData.PageSize) + 1;
        }


        if (PageIndex <= 0) { PageIndex = 1; }
        if (PageIndex > _PageData.PageCount) { PageIndex = _PageData.PageCount; }

        _PageData.CurrentPageIndex = PageIndex;

        List<T_GuestBook> _List2 = _List.Skip((PageIndex - 1) * PageSize).Take(PageSize).ToList();//取得当前页数据

        _PageData.Data = _List2;

        return JavaScriptConvert.SerializeObject(_PageData);

    }

注意,这里我们返回的是string类型,并且是用JavaScriptConvert.SerializeObject处理后的JSON字符串,至于WebInvoke(ResponseFormat = WebMessageFormat.Json这里为什么要加Json返回格式,原因很简单,不指定Json格式,默认就是以xml返回的,会无端在前后加上更多无用字符 5.ExtJs前端完整代码:

Code
 <html>
<head>
    <title>ExtJs.XTemplate + WCF 打造无刷新数据分页</title>   
    <script type="text/javascript" src="../adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="../ext-all-debug.js"></script>
    <style type="text/css">
        *{font-size: 9pt;line-height: 120%;}
        .red{color: Red;}
        h1{font-size:16px;}
    </style>
</head>
<body>

    <script type="text/javascript">
        Ext.onReady(function() {           

            //定义XTemplate模板组件
            var tpl = new Ext.XTemplate(
'<table width="100%">',
'<tpl for="Data">', //表明这里开始循环
'<tr><td style="font-weight:bold;color:#666">网友[{F_IP}] {F_Date:date("Y-m-d H:i:s")} 说:</td></tr>',
'<tr><td>  {F_Content}</td></tr>',
'<tr><td><hr style="height:1px"/></td></tr>',
'</tpl>', //循环结束
'</table>',
'<div id="Page">', //这里开始是分页部分
'<span style="float:left">当前:第<span class="red">{CurrentPageIndex}</span>页/共<span class="red">{PageCount}</span>页,<span class="red">{PageSize}</span>条/每页,共<span>{RecordCount}</span>条</span>',
'<span style="float:right"><a href="javascript:;" id="btnFirst">首页</a>|<a href="javascript:;" id="btnPre">上页</a>|<a href="javascript:;" id="btnNext">下页</a>|<a href="javascript:;" id="btnLast">尾页</a>',
' <input type="text" id="GoPage" style="width:20px" value="{CurrentPageIndex}"/><button id="btnGo">Go</button></span>',
'</div>'
);



            //初始化分页变量 
            var iPageSize = 4;
            var iCurrentPageIndex = 1;
            var iRecordCount = 0;
            var iPageCount = 0;

            //加载数据函数
            function loadData(pageSize, currentPageIndex) {
                //alert("pageSize:" + pageSize + " , currentPageIndex:" + currentPageIndex);
                var BBS = Ext.get("BBS");
                BBS.dom.innerHTML = "数据加载中,请稍候";
                BBS.dom.className = "red";
                Ext.Ajax.request({
                    url: "../WCF/MyService.svc/GetData2",
                    params: { PageSize: pageSize, PageIndex: currentPageIndex },
                    method: "GET",
                    success: function(request) {
                        BBS.dom.className = "";
                        
                        //转换服务器端返回字符串的格式
                        var data = request.responseText;
                        data = data.substr(1);
                        data = data.substr(0, data.length - 1);
                        data = data.replace(/\\\"/g, '"');                        
                        data = Ext.util.JSON.decode(data)

                        tpl.overwrite("BBS", data);


                        //其实下面这四句在本例中没什么大的作用,不过可能以后改进时会用到,暂时先保留吧
                        iPageSize = data.PageSize;
                        iCurrentPageIndex = data.CurrentPageIndex;
                        iRecordCount = data.RecordCount;
                        iPageCount = data.PageCount;

                        //alert("pageSize:" + iPageSize + " , currentPageIndex:" + iCurrentPageIndex);

                        //开始处理分页按钮/链接事件
                        var oBtnGo = Ext.get("btnGo");
                        var oGoPage = Ext.get("GoPage");
                        oBtnGo.on("click", function() {
                            loadData(iPageSize, oGoPage.dom.value);
                        });

                        var oBtnFirst = Ext.get("btnFirst"); //第一页
                        oBtnFirst.on("click", function() { loadData(iPageSize, 1); })


                        var oBtnPre = Ext.get("btnPre"); //上一页
                        oBtnPre.on("click", function() { loadData(iPageSize, iCurrentPageIndex - 1); })

                        var oBtnNext = Ext.get("btnNext"); //下一页
                        oBtnNext.on("click", function() { loadData(iPageSize, iCurrentPageIndex + 1); })

                        var oBtnLast = Ext.get("btnLast"); //最后一页
                        oBtnLast.on("click", function() { loadData(iPageSize, iPageCount); })

                    },

                    failure: function() {
                        BBS.dom.innerHTML = "数据加载失败,请尝试刷新!";
                    }
                });
            }

            //载入数据
            loadData(iPageSize, iCurrentPageIndex);

        });
    </script>
    <h1>ExtJs.XTemplate + WCF 打造无刷新数据分页</h1>
    <div id="BBS">
        <!-- 模板参考部分
        <table>
            <tr>
                <td style="font-weight: bold; color: #666">
                    网友[127.0.0.1] 2007-9-1:00 说:
                </td>
            </tr>
            <tr>
                <td>
                    这是第一条留言
                </td>
            </tr>
            <tr>
                <td>
                    <hr style="height: 1px" />
                </td>
            </tr>
        </table>
        -->
    </div>    
    
</body>
</html>

这里有几个要点: (1).datetime在xtemplate中的格式化写法:{F_Date:date("Y-m-d H:i:s")} (2).服务端返回字符格式的处理: 因为JavaScriptConvert.SerializeObject(_PageData)这里已经成功序列化了,但是wcf的服务在返回时,必须要有一种格式,要么xml,要么json,所以我们指定了wcf以json格式返回后,会对正常的结果再做一次序列化,最后的结果是使字符串前后都加上了双引号,同时把原来正常的双引号做了转义处理,参考下面的:

正常的Json字符串: {"RecordCount":6,"PageSize":3,"PageCount":2,"CurrentPageIndex":1,"Data":[{"F_ID":1,"F_IP":"192.23.37.41","F_Date":new Date(1221052494578),"F_Content":"这是第一条留言","F_Reply":""},{"F_ID":2,"F_IP":"192.168.0.1","F_Date":new Date(1221052494578),"F_Content":"这是第二条留言","F_Reply":""},{"F_ID":3,"F_IP":"192.168.0.2","F_Date":new Date(1221052494578),"F_Content":"这是第三条留言","F_Reply":""}]}

服务器返回的Json字符串: "{\"RecordCount\":6,\"PageSize\":3,\"PageCount\":2,\"CurrentPageIndex\":1,\"Data\":[{\"F_ID\":1,\"F_IP\":\"192.23.37.41\",\"F_Date\":new Date(1221052494578),\"F_Content\":\"这是第一条留言\",\"F_Reply\":\"\"},{\"F_ID\":2,\"F_IP\":\"192.168.0.1\",\"F_Date\":new Date(1221052494578),\"F_Content\":\"这是第二条留言\",\"F_Reply\":\"\"},{\"F_ID\":3,\"F_IP\":\"192.168.0.2\",\"F_Date\":new Date(1221052494578),\"F_Content\":\"这是第三条留言\",\"F_Reply\":\"\"}]}"

所以我们要处理一下,关键代码: //转换服务器端返回字符串的格式                         var data = request.responseText;                         data = data.substr(1);                         data = data.substr(0, data.length - 1);                         data = data.replace(/\\\"/g, '"');                                                data = Ext.util.JSON.decode(data)

(3).为使分页按钮有效,我们需要在成功返回服务端数据后,为每个分页链接以及按钮加上onClick事件,即这一部分 //开始处理分页按钮/链接事件                         var oBtnGo = Ext.get("btnGo");                         var oGoPage = Ext.get("GoPage");                         oBtnGo.on("click", function() {                             loadData(iPageSize, oGoPage.dom.value);                         });

                        var oBtnFirst = Ext.get("btnFirst"); //第一页                         oBtnFirst.on("click", function() { loadData(iPageSize, 1); })

                        var oBtnPre = Ext.get("btnPre"); //上一页                         oBtnPre.on("click", function() { loadData(iPageSize, iCurrentPageIndex - 1); })

                        var oBtnNext = Ext.get("btnNext"); //下一页                         oBtnNext.on("click", function() { loadData(iPageSize, iCurrentPageIndex + 1); })

                        var oBtnLast = Ext.get("btnLast"); //最后一页                         oBtnLast.on("click", function() { loadData(iPageSize, iPageCount); })

   另外这一段代码的位置,也要留意一下,不能写在其它地方:比如Ajax请求之后,因为当Ajax还未成功返回数据/XTemplate未成功更新时,分页按钮以及链接还没有加载到页面中,这时如果用Ext.get()取对象,Js会报错

   完成了,我们来看下一效果:

转载请注明来自"菩提树下的杨过"

最后讲一点题外话:    做完这个后,我在想:单就这个示例而言,这跟直接用asp.net ajax的updatePannel有什么区别,有什么优势呢?相信也有不少人跟我有一样的疑问,后来我想了想,至少有二个好处:    a.updatepannel默认会引起大量的数据回发,虽然页面没刷新,但是客户端跟服务端之间的传输数据量很大,而用ExtJs+Wcf,除了wcf返回的字符串,就没其它东西了,性能上会提高    b.相对而言,ExtJs的Ajax请求方式,我觉得比aspx.net ajax的更容易操作.

附:源文件下载(解压后demo/list.html即为运行的演示页面,开发环境:vs2008 sp1 + win2003)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏岑玉海

RavenDb学习(九)高级特性下半部分

1.聚合缓存 RavenDb默认是缓存所有的请求url的,最大的缓存请求数默认是2048 documentStore.Conventions.ShouldCa...

3055
来自专栏bluesummer

StackExchange.Redis学习笔记(二) Redis查询 五种数据类型的应用

ConnectionMultiplexer ConnectionMultiplexer 是StackExchange.Redis的核心对象,用这个类的实例来进行...

3529
来自专栏技术小讲堂

ASP.NET AJAX(4)__客户端访问WebService服务器端释放WebService方法客户端访问WebService客户端访问PageMethod错误处理复杂数据类型使用基础客户端代理的

服务器端释放WebService方法 编写一个普通的WebService 为WebService类添加自定义的属性标记__ScriptServiceAttrib...

2787
来自专栏菩提树下的杨过

ExtJs学习笔记(23)-ScriptTagProxy+XTemplate+WCF跨域取数据

ajax应用中跨域一直是一个非常麻烦的问题,目前也有一些解决办法,但要么比较麻烦,要么就不具备通用性,幸好ExtJs里的ScriptTagProxy提供了跨域读...

2408
来自专栏逸鹏说道

C#注册表情缘

记得当时刚接触C#的时候,喜欢编写各种小软件,而注册表系列和网络系列被当时的我认为大牛的必备技能。直到我研究注册表前一天我都感觉他是那么的高深。 今天正好有空,...

3408
来自专栏张善友的专栏

自定义Unity对象生命周期管理集成ADO.NET Entity Framework

在Unity中,从Unity 取得的实例为 Transient。如果你希望使用多线程方式,就需要在组成时使用lifecycle参数,这时候取出的组件就不再是同一...

3268
来自专栏码农阿宇

.Net Core中利用TPL(任务并行库)构建Pipeline处理Dataflow

在学习的过程中,看一些一线的技术文档很吃力,而且考虑到国内那些技术牛人英语都不差的,要向他们看齐,所以每天下班都在疯狂地背单词,博客有些日子没有更新了,见谅见谅...

991
来自专栏菩提树下的杨过

XmlSpy / XSD 以及 验证

很早以前看过一句话:“XML就象空气”,在企业应用开发中XML是一个重要的数据交换标准。而XSD则可以用来校验XML的数据格式是否正确。 一个典型的XSD文件如...

21810
来自专栏Java大联盟

Java爬虫入门

5373
来自专栏xiaoheike

Eclipse下maven使用嵌入式(Embedded)Neo4j创建Hello World项目

代码可以在github上看到:https://github.com/neo4j/neo4j/blob/2.3.3/community/embedded-exam...

1072

扫码关注云+社区