Ext.data
Ext.data在命名空间中定义了一系列store、reader和proxy。Grid和ComboxBox都是以Ext.data为媒介获取数据的,它包含异步加载、类型转换、分页等功能。Ext.data默认支持Array、JSON、XML等数据格式,可以通过Memory、HTTP、ScriptTag等方式获得这些格式的数据。如果要实现新的协议和新的数据结构,只需要扩展reader和proxy即可。DWRProxy就实现了自身的proxy和reader,让EXT可以直接从DWR获得数据。
Ext.data最主要的功能是获取和组织数据结构,并和特定控件联系起来,于是Ext.data成了数据的来源,而控件负责显示数据;;;
以下为Ext.data命名空间中的在个常用类:
1、DataProxy
2、DataReader
3、Store
关于数据的显示,一般要经历三个流程:DataProxy-->DataReader-->Store
一、Ext.data.Connection
Ext.data.Connection是对Ext.lib.Ajax的封装,它提供了配置使用Ajax的通用方式,它在内部通过Ext.lib.Ajax实现与后台的异步调用。与底层的Ext.lib.Ajax相比,Ext.data. Connection提供了更简洁的配置方式,使用起来更方便。
Ext.data.Connection主要用于在Ext.data.HttpProxy和Ext.data.ScriptTagProxy中执行与后台交互的任务,它会从指定的URL获得数据,并把后台返回的数据交给HttpProxy或ScriptTagProxy处理,Ext.data.Connection的使用方式如代码清单10-1所示。
代码清单10-1 使用Ext.data.Connection
var conn = new Ext.data.Connection({
autoAbort: false,
defaultHeaders: {
referer: 'http://localhost:8080/'
},
disableCaching : false,
extraParams : {
name: 'name'
},
method : 'GET',
timeout : 300,
url : '01-01.txt'
});
在使用Ext.data.Connection之前,都要像上面这样创建一个新的Ext.Connection实例。我们可以在构造方法里配置对应的参数,比如autoAbort表示链接是否会自动断开、default- Headers参数表示请求的默认首部信息、disableCaching参数表示请求是否会禁用缓存、extraParams参数代表请求的额外参数、method参数表示请求方法、timeout参数表示连接的超时时间、url参数表示请求访问的网址等。
在创建了conn之后,可以调用request()函数发送请求,处理返回的结果,如下面的代码所示。
conn.request({
success: function(response) {
Ext.Msg.alert('info', response.responseText);
},
failure: function() {
Ext.Msg.alert('warn', 'failure');
}
});
Request()函数中可以设置success和failure两个回调函数,分别在请求成功和请求失败时调用。请求成功时,success函数的参数就是后台返回的信息。
我们再来看一下request函数中的其他参数。
q url:String:请求url。
q params:Object/String/Function:请求传递的参数。
q method:String:请求方法,通常为GET或POST。
q callback:Function:请求完成后的回调函数,无论是成功还是失败,都会执行。
q success:Function:请求成功时的回调函数。
q failure:Function:请求失败时的回调函数
q scope:Object:回调函数的作用域。
q form:Object/String:绑定的form表单。
q isUpload:Boolean:是否执行文件上传。
q headers:Object:请求首部信息。
q xmlData:Object:XML文档对象,可以通过URL附加参数的方式发起请求。
q disableCaching:Boolean:是否禁用缓存,默认为禁用。
Ext.data.Connection还提供了abort([Number transactionId])函数,当同时有多个请求发生时,根据指定的事务id放弃其中的某一个请求。如果不指定事务id,就会放弃最后一个请求。isLoading([Number transactionId])函数的用法与abort()类似,可以根据事务id判断对应的请求是否完成。如果未指定事务id,就判断最后一个请求是否完成。
二、Ext.data.Record
Ext.data.Record就是一个设定了内部数据类型的对象,它是Ext.data.Store的最基本组成部分。如果把Ext.data.Store看作是一张二维表,那么它的每一行就对应一个Ext.data. Record实例。
Ext.data.Record的主要功能是保存数据,并且在内部数据发生改变时记录修改的状态,它还可以保留修改之前的原始值。
我们使用Ext.data.Record时通常都是由create()函数开始,首先用create()函数创建一个自定义的Record类型,如下面的代码所示。
var PersonRecord = Ext.data.Record.create([
{name: 'name', type: 'string'},
{name: 'sex', type: 'int'}
]);
PersonRecord就是我们定义的新类型,包含字符串类型的name和整数类型的sex两个属性,然后我们使用new关键字创建PersonRecord的实例,如下面的代码所示。
var boy = new PersonRecord({
name: 'boy',
sex: 0
});
创建对象时,可以直接通过构造方法为对象赋予初始值,将'boy'赋值给name,0赋值给sex。
现在,我们得到了PersonRecord的实例boy,如何才能得到它的属性呢?以下三种方式都可以获得boy中name属性的数据,如下面的代码所示。
alert(boy.data.name);
alert(boy.data['name']);
alert(boy.get('name'));
这里涉及Ext.data.Record的data属性,这是定义在Ext.data.Record中的一个公共属性,用于保存当前record对象的所有数据。它是一个JSON对象,可以直接从它里面获得需要的数据。可以通过Ext.data.Record的get()函数方便地从data属性中获得指定的属性值。
如果我们需要修改boy中的数据,请不要使用以下方式直接操作data,如下面的代码所示。
boy.data.name = 'boy name';
boy.data['name'] = 'boy name';
而应该使用set()函数,如下面的代码所示。
boy.set('name', 'body name');
set()函数会判断属性值是否发生了改变,如果改变了,就要将当前对象的dirty属性设置为true,并将修改之前的原始值放入modified对象中,供其他函数使用。如果直接操作data中的值,record就无法记录属性数据的修改情况。
Record的属性数据被修改后,我们可以执行如下几种操作。
q commit()(提交):这个函数的效果是设置dirty为false,并删除modified中保存的原始数据。
q reject()(撤销):这个函数的效果是将data中已经修改了的属性值都恢复成modified中保存的原始数据,然后设置dirty为false,并删除保存原始数据的modified对象。
q getChanges()获得修改的部分:这个函数会把data中经过修改的属性和数据放在一个JSON对象里并返回。例如上例中,getChanges()返回的结果是{name:’body name’}。
q 我们还可以调用isModified()判断当前record中的数据是否被修改。
Ext.data.Record还提供了用于复制record实例的函数copy()。
var copyBoy = boy.copy();
这样我们就得到了boy的一个副本,它里面包含了boy的data数据,但copy()函数不会复制dirty和modified等额外的属性值。
三、DataProxy
获取想要的数据,通过他能得到来自不同地方的数据,如数组、远程服务器等,并组织成不同的格式。
Ext.data.DataProxy是获取数据的代码,数据可能来于内存,可能来自于同一域的远程服务器数据,更有可能来自于不同域的远程服务器数据。
DataProxy的代码就是一空架子。且看:
Ext.data.DataProxy = function(){
this.addEvents(
'beforeload',
'load'
);
Ext.data.DataProxy.superclass.constructor.call(this);
};
Ext.extend(Ext.data.DataProxy, Ext.util.Observable);
就是加两事件,从Observable继承了
但在实际应用中,不会直接使用Ext.data.DataProxy类,而是使用他的子类:
1、MemoryProxy:获取来自内存的数据,可以是数组、json或者xml
2、HttpProxy:使用HTTP协议通过ajax从远程服务器获取数据的代理,需要指定url
3、ScriptTagProxy:功能和HttpProxy一样,但支持跨域获取数据。
(一)、MemoryProxy
MemoryProxy只能从JavaScript对象获得数据,可以直接把数组,或JSON和XML格式的数据交给它处理,如下面的代码所示。
var proxy = new Ext.data.MemoryProxy([
['id1','name1','descn1'],
['id2','name2','descn2']
]);
例如:定义一个用来保存城市名称的二维数组:
Var cities = [
[1,"长沙市"],
[2,"株洲市"],
[3,"湘潭市"],
[4,"邵阳市"]
];
在二维数组中,每一个城市保存了两个值:值一表示城市编号,作为实际值,值二表示城市名称,作为显示值,然后将其构建出一个MemoryProxy对象:
var proxy = new Ext.data.MemoryProxy(cities);
(二)、HttpProxy
HttpProxy使用HTTP协议,通过Ajax去后台取数据,构造它时需要设置url:'xxx.jsp'参数。这里的url可以替换成任何一个合法的网址,这样HttpProxy才知道去哪里获取数据,如下面的代码所示。
var proxy = new Ext.data.HttpProxy({url:'xxx.jsp'});
HttpProxy用于远程代理,而且服务端返回信息时必须指定Content-Type属性为"text/xml"或"application/x-json"
后台需要返回EXT所需要的JSON格式的数据,下面的内容就是后台使用JSP的一个范例,如下面的代码所示。
HttpProxy( Object conn ) 构造一个HttpProxy对象,参数可以是一个类似于{url: 'foo.php'}这样的json对象,也可以是一个Ext.data.Connection对象,如果参数没有指定,将使用Ext.Ajax对象将被用于发起请求
getConnection() : Connection 得到当前连接对象
load( Object params, Ext.data.DataReader reader, Function callback, Object scope, Object arg ) : void
从配置的connection对象得到record数据块,并激发callback
params: 发起http请求时所要传递到服务端的参数
DataReader: 见DataReader
callback: 回叫方法,第一个参数为接收到的信息,第二个参数为arg,第三个是成功标志
scope: 范围
arg: 这儿的参数将会传递给回叫函数callback
使用示例:
var proxy=new Ext.data.HttpProxy({url:'datasource.xml'});
//关于reader将会在Ext.data.DataReader中讲解
var reader = new Ext.data.XmlReader({
totalRecords: "results",
record: "row",
id: "id"
}, [
{name: 'name', mapping: 'name'},
{name: 'occupation'}
]);
//定义回叫方法
var metadata;
function callback(data,arg,success){
if(success){
metadata=data;
}
}
//从connection配置的url中利用reader将返回的xml文件转为元数据,并传递给callback
proxy.load( null,reader,callback,this);
response.setContentType("application/x-json");
Writer out = response.getWriter();
out.print("[" +
"['id1','name1','descn1']" +
"['id2','name2','descn2']" +
"]");
(三)、ScriptTagProxy
ScriptTagProxy的用法几乎和HttpProxy一样,如下面的代码所示。
var proxy = new Ext.data.ScriptTagProxy({url:'xxx.jsp'});
从这里也看不出来它是如何支持跨域的,我们还需要在后台进行相应的处理,如下面的代码所示
String cb = request.getParameter("callback");
response.setContentType("text/javascript");
Writer out = response.getWriter();
out.write(cb + "(");
out.print("[" +
"['id1','name1','descn1']" +
"['id2','name2','descn2']" +
"]");
out.write(");");
其中的关键就在于从请求中获得的callback参数,这个参数叫做回调函数。ScriptTag- Proxy会在当前的HTML页面里添加一个<script type="text/javascript"src="/xxx.jsp"> </script>标签,然后把后台返回的内容添加到这个标签中,这样就可以解决跨域访问数据的问题。为了让后台返回的内容可以在动态生成的标签中运行,EXT会生成一个名为callback的回调函数,并把回调函数的名称传递给后台,由后台生成callback(data)形式的响应内容,然后返回给前台自动运行。
虽然上述处理过程比较难理解,但是我们只需要了解ScriptTagProxy的用法就足够了。如果还想进一步了解ScriptTagProxy的运行过程,可以使用Firebug查看动态生成的HTML以及响应的JSON内容。
最后我们来分析一下EXT的API文档中提供的示例,这段后台代码会自动判断请求的类型,返回支持ScriptTagProxy或HttpProxy的数据,如代码清单10-2所示。
代码清单 在后台同时支持HttpProxy和ScriptTagProxy
boolean scriptTag = false;
String cb = request.getParameter("callback");
if (cb != null) {
scriptTag = true;
response.setContentType("text/javascript");
} else {
response.setContentType("application/x-json");
}
Writer out = response.getWriter();
if (scriptTag) {
out.write(cb + "(");
}
out.print(dataBlock.toJsonString());
if (scriptTag) {
out.write(");");