ExtJs学习笔记
目录
1. ExtJs 结构树 2
2. 对ExtJs的态度 3
3. Ext.form概述 4
4. Ext.TabPanel篇 5
5. Function扩展篇 7
6. Ext.data.Store篇 10
7. Ext.data.JsonReader篇一 12
8. Ext.data.JsonReader篇二 15
9. Ext.data.HttpProxy篇 19
10. Ext.data.Connection篇一 20
11. Ext.data.Connection篇二 24
12. Ext.Updater篇一 26
13. Ext.Updater篇二 27
14. JSON序列化篇 33
15. 通信篇 35
16. extJs 2.0学习笔记(Ajax篇) 38
17. extJs 2.0学习笔记(Ext.data序论篇) 39
18. extJs 2.0学习笔记(Ext.Panel终结篇) 40
19. extJs 2.0学习笔记(事件注册总结篇) 45
20. extJs 2.0学习笔记(Ext.Panel篇一) 48
21. extJs 2.0学习笔记(Ext.Panel篇二) 50
22. extJs 2.0学习笔记(Ext.Panel篇三) 59
23. extJs 2.0学习笔记(Ext.Panel篇四) 62
24. extJs 2.0学习笔记(组件总论篇) 66
25. extJs 2.0学习笔记(Ext.Element API总结) 69
26. extJs 2.0学习笔记(Element.js篇) 73
27. extJs 2.0学习笔记(DomHelper.js篇) 76
28. extJs 2.0学习笔记(ext.js篇) 77
1. ExtJs 结构树
2. 对ExtJs的态度
extjs的确是个好东西,但是,它的优点也就是它的缺点:
• 加载页面慢
• 时间一长,浏览器占内存就会疯长
• 服务器端功能极度削弱,除了数据库操作外,几乎所有功能都到了客户端,还得用javascript来写。
• 功能全到了客户端,而客户端语言javascript的编写还是有许多的麻烦,真正精通它的人极少。
• javascript对于大部分人来说,不易调试
• 大量存在的javascript代码难以维护
• 开发速度极慢。
• extjs本身还存在一些问题
正是因为有这么多的问题,老板们都得掂量一下了。用它倒底值不值。当然,这儿也得说一下它的优点:
• 因为一切都是javascript搞定,所以,界面上的问题再也不像以前一样让人郁闷了,客户端对界面的的操作取得极大的便利,而不像以前一样,服务器端生成n多垃圾代码,以前的时代就彷佛隔靴搔痒,服务器端企图布置好一切。现在不同了,客户端用一个Ext.Ajax.Request请求数据方便,然后,显示出来也容易。
• 又回到了c/s时代。c/s让人神往啊。web该死的无状态让人郁闷
• 学习extjs的一个极大的好处,所有当前web开发界面上的需求都可以在这儿找到答案。通过研究它的代码,我们可以开发出自己的ajax框架来,可以写出适合于自己的widgets来。而不用背着extjs那个大乌龟壳。
我认为,不宜用extjs来开发整个应用,但是,在极为需要的地方用一用,还是蛮好的,整个站点都用它那就麻烦了。现在我对于选择ajax框架有了一点心得。
不要使用extjs来开发,但是,一定要学习、研究它,研究它之后才会晓得,我们写代码应当这么写才优美、才合适。研究了它后就应当选一款轻量型的框架了。然后自己写组件。用以取代:Ext.Window、Ext.TabPanel、Ext.Panel这些好东西。
研究了extjs,我敢说:一览众山小啊!什么prototype、dojo、jQuery之类,就容易多了。
真正要用的ajax框架,我看,倒不如选择prototype,它是个轻量型,我觉得,一个ajax,只要封装了三个东西就行了:
一、Element。把dom元素要封装一下,加入动画、求取、设置各种参数值的功能
二、XMLHttpRequest,要把它封装一下,这个所有框架都做了
三、把事件机制要封装一下,最好像extjs一样,xxx.on('click',function(){});就成了。
有了这三个就差不多了,那些什么window、tabs,网上多的是代码,搞些下来改篇改篇就成了。
关于prototype,我找到了它的中文文档(1.5的),1.5的大小是93.7k,事实上,这个大小还可以缩小,可以使用工具去掉多余的空格,差不多了。
3. Ext.form概述
Ext.form中封装了是输入组件。input、textArea、frameSet、form等元素都被包装起来了。我刚才发了点时间对它的类图分析了一下,用StartUML做了图如下:
Ext.form中的组件太多,实在不大
4. Ext.TabPanel篇
Ext.TabPanel这个东西是最常用的组件之一,它继承自Ext.Panel。看了一个下午的源代码,对它的一些基本原理有所了解了。
下面要讲一些问题,这些问题绝对是本人独门秘笈,非入室弟子不传。哈哈哈。
一、组件的组成:
因为继承自Ext.Panel,所以,它也是由header、tbar、body、bbar、footer这几个部分构成,有人问:TabPanel的面板标签是在哪儿呢(就是你点击换页的东西)?它默认是放在header中的。但是,如果设置了:tabPosition的话就不一定了,tabPosition可取两个值:top、bottom。所以,标签可以是放在下面,但是,Ext目前还不支技放在左边、右边。
那么,不同的标签是用什么元素来组织的呢?用ul。一页对应一个li。li的id的取值有规律哦,它的取值公式如下:tabpanel.id+tabpanel.idDelimiter+面板的id。正是因为有了这个规律,才能根据点击的标签而找到对应的面板。这个问题是一大主题,在下面讲。
这是面板的标签,下面的面板呢?简单!!!一个Ext.Panel对应一个面板,注意:这儿的面板是没有header的,如果你想tab.items.get(1).header,在这儿,header===undefined。为什么为面板定义的title会对应到标签中去呢?这个是TabPanel的特意处理的。至于换页效果是怎么出来的?CardLayout。这下组件的大概结构都清楚了。还有不明白,自己new Ext.TabPanel({……})一个,然后在FireBug下面去查看dom结构,就一清二楚了。
二、处理标签的事件
为什么要研究这个问题?有需求的,如何在鼠标移到标签上时就显示对应的面板呢?默认情况下,TabPanel是不支持这个功能的,但是,这个功能有时是需要的。这儿有点小技巧。
看Ext.TabPanel源代码中关于标签的事件处理:
this.strip.on('mousedown', this.onStripMouseDown, this);
this.strip.on('click', this.onStripClick, this);
this.strip.on('contextmenu', this.onStripContextMenu, this);
if(this.enableTabScroll){
this.strip.on('mousewheel', this.onWheel, this);
}
这段代码写在initEvents函数中,先解释一下,this.strip是指头部放标签的那个ul元素,相信,98%的读者会想,要注册事件也应当是为li元素注册,怎么会统统注册到ul这个父容器上面呢?原理就是事件冒泡。关于事件传递的原理,本人在下一文中有详细的实验、明确的结论,不再赘言。
ul元素捕获了事件,怎样在事件处理函数中得知倒底是哪个li发生了事件呢?Ext写了个函数:findTargets。详情请见如下代码:
findTargets : function(e){
var item = null;
var itemEl = e.getTarget('li', this.strip);
if(itemEl){
item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
if(item.disabled){
return {
close : null,
item : null,
el : null
};
}
}
return {
close : e.getTarget('.x-tab-strip-close', this.strip),
item : item,
el : itemEl
};
},
// private
onStripMouseDown : function(e){
e.preventDefault();
if(e.button != 0){
return;
}
var t = this.findTargets(e);
if(t.close){
this.remove(t.item);
return;
}
if(t.item && t.item != this.activeTab){
this.setActiveTab(t.item);
}
},
一切的关键就在于li元素的id的命名规则,从中取出对应的面板的id,这样就能getComponent,从而获得对应的面板引用,再setActiveTab就办成了。至于getTarget这个是EventObject中封装的函数,作用是在事件传播路径上查找满足指定选择条件的元素。这个函数的详情见它的源码。
到了这里,之前所讲的鼠标悬停问题只要依照方面方法解决就是了,切记,不要处理mouseout事件,不然,事情就麻烦了,详情见我以前写过的关于mouseover事件的一篇文章。
5. Function扩展篇
ExtJs对JavaScript的内建对象进行了扩展,对什么Object、Date、Array、Function、String的扩展,扩展方法想必诸位都烂熟于心了:用prototype的办法。这一篇讲一讲Function扩展的精妙之处,之所以突然研究这个问题,是因为我在研究Ext.data.Store的源代码时,看到一行代码:
this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
当初,我在研究Ext.js中的代码时,对于Function的几个扩展想不透、看不明,今日大悟。且见扩展的源代码:
createDelegate : function(obj, args, appendArgs){
var method = this;
return function() {
var callArgs = args || arguments;
if(appendArgs === true){
callArgs = Array.prototype.slice.call(arguments, 0);
callArgs = callArgs.concat(args);
}else if(typeof appendArgs == "number"){
callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
var applyArgs = [appendArgs, 0].concat(args); // create method call params
Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
}
return method.apply(obj || window, callArgs);
};
},
createDelegate函数的作用是,为指定函数创建一个回调函数,注意是创建一个新的函数返回,它返回的是一个新函数。我以前一直不明白,为什么要这么做,就像上面红色的那行代码,相信大伙与我一样,都在想,为什么不是写成这样:
this.reader.onMetaChange=this.onMetaChange;
不是应当这样写的吗?如果用过dotnet,那么委托一定是晓得了,javascript中的函数跟c#的委托一样,有很相近的意义,为什么c#中能这样写,JavaScript中不能这样写呢?
一切都因为this,this这个东西见风使舵,像上面onMetaChange这函数,实际调用时是在reader中,那么如果onMetaChange中使用了this关键字,那么,this是指向reader的,而不是指向onMetaChange的定义环境所对应的this。而事实上,我们往往想把这个this指向函数的定义环境,这也正是回调的最招人喜欢的地方,然而,因为this的问题,回调就不能像上面那样直接赋值。还得做些手脚,得让函数调用时scope为当前定义环境。
改变一个函数执行的scope,熟翻JavaScript的兄弟一定晓得要用:call、apply。至此,createDelegate的产生背景、作用都作了个交代。
createDelegate(this),调用时,一般直接传个this就行了,当真是妙啊。事实上,我上面讲的一通道理清楚了,这个函数的代码就没有秘密可言了。关键就是一个this。我现在感叹,你对JavaScript的造诣与你对this的领悟层次成正比。
既然讲了createDelegate,其他几个扩展函数一并讲了。
createCallback : function(/*args...*/){
// make args available, in function below
var args = arguments;
var method = this;
return function() {
return method.apply(window, args);
};
}
也是创建调用者的回调,不过,回调函数的scope为window。相当于createDelegate(window)。没什么讲的。
defer : function(millis, obj, args, appendArgs){
var fn = this.createDelegate(obj, args, appendArgs);
if(millis){
return setTimeout(fn, millis);
}
fn();
return 0;
},
此函数调用一次就让函数延迟调用一次。对setTimeout的封装罢了。如果没有定义延时参数,那么就马上执行。这个函数也没有技术性可言。
createSequence : function(fcn, scope){
if(typeof fcn != "function"){
return this;
}
var method = this;
return function() {
var retval = method.apply(this || window, arguments);
fcn.apply(scope || this || window, arguments);
return retval;
};
},
这个函数就有点意思了,刚开始研究ext.js的时候还没有看明白,它的作用是在返回一个函数,此函数先调用“调用函数”,后调用传递进来的函数。这句话可能还没说清,见示例如下:
function A(){alert("第一个执行!");return 1;}
function B(){alert("第二个执行!");return 2;}
function C(){alert("第三个执行!");return 3;}
var D=A.createSequence(B).createSequence(C);
var result=D();
上面代码产生的效果是:
第一弹出框显示:第一个执行!
第二弹出框显示:第二个执行!
第三弹出框显示:第三个执行!
result的值为:3。
这下子诸位都明白了吧。用过dotnet的知道,委托变量有这种类似的功能。就是累加执行的效果。
createInterceptor : function(fcn, scope){
if(typeof fcn != "function"){
return this;
}
var method = this;
return function() {
fcn.target = this;
fcn.method = method;
if(fcn.apply(scope || this || window, arguments) === false){
return;
}
return method.apply(this || window, arguments);
};
}
这个函数也有点意思,有创意,它返回被调用函数的回调,这个回调是条件执行的,执行条件是createInterceptor传入的那个函数返回真。示例代码如下:
function A(){}
var B=A.createInterceptor(function(i){return i>0;});
B(1),则A被执行,如果调用B(-1),A则不被执行。B的作用就是如果传入的第一个参数的值大于0时A才被执行,否则不执行。
相当于原有函数的功能不变,只是加个执行条件。这个想法着实巧妙。这一招现在想来,也可以用到c#中。
6. Ext.data.Store篇
Ext.data.Store,这个东西是JavaScript版的DataTable啊。貌似其他Ajax框架都没有这个玩意啊。可见啦,Ext是真的打算把b/s开发重新变成c/s开发啊。哈哈哈。便宜我等了。待某细研之。
Store类提供对记录集(Record)的包装,通过前面的研究可知,DataProxy取数据(url或数组或xml或json),DataReader用于从不规范的数据取出并格式化指定结构的记录集。记录的结构由Record.create创建。
DataProxy通过对Connection的调用取得数据(Response)后,在回调中调用DataReader的read函数,从而把response中的数据解析成记录集,这个记录集将再以回调参数的形式传出来,store实现这个回调,并把里面的Recodrd[]取出来,放到data这个成员中。store.data是一个MixedCollection对象,MixedCollection作什么用的前面也讲过,它本质就是一个容器,ExtJs确实很好,连容器类都写了。
有了store.data,数据进了这儿,就好办了,store调用MixedCollection的功能,实现了一些通用的函数,如取指定成员、查询、遍历、事务等等,这些都不足道。什么提交修改、取消修改的功能却是根源于Record。Record类自身就封装了这个功能,Store中只是再次封装罢了,这个原理也很简单。看代码即知。
上面讲的是通用原理,是大概,下面拣紧要的代码说一下。
它定义了构造函数,继承自Ext.Observable。第一行代码就是个重点:
this.data = new Ext.util.MixedCollection(false);
这是定义data,所有记录都将保存在它里面。
this.baseParams = {};
// private
this.paramNames = {
"start" : "start",
"limit" : "limit",
"sort" : "sort",
"dir" : "dir"
};
baseParams将在调用HttpProxy时用到,它将作为params附加到url末尾。这个东西没有悬念。至于paramsNames用于保存参数名,start、limit应当用于分页,sort、dir用于排序,不过,我看了通篇的代码,发现,Store本身不提供任何其他分页、排序功能的实现,还是得依靠服务器端的。只不过,这儿提供一种统一的方式罢了。
if(config && config.data){
this.inlineData = config.data;
delete config.data;
}
意思是说,如果创建store时,设了config,且config.data存在,那么,将直接从config.data中loadData。构造函数后面一点就有。inlineData这个属性没活多久就被delete了。
if(this.url && !this.proxy){
this.proxy = new Ext.data.HttpProxy({url: this.url});
}
if(this.reader){ // reader passed
if(!this.recordType){
this.recordType = this.reader.recordType;
}
if(this.reader.onMetaChange){
this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
}
}
if(this.recordType){
this.fields = this.recordType.prototype.fields;
}
就是根据config中的情况,创建成员:proxy,reader,recordType,onMetaChange。这了这四个,就好方便在下面定义的load中加载数据并完全记录集的封装。说出来一文不值。
this.modified = [];
这个东西用于保存那些有修改过的记录的旧值。之所以能取消修改,正是源于此啊。
关于addEvents那个语句,就没必要讲了,大伙都懂。
if(this.proxy){
this.relayEvents(this.proxy, ["loadexception"]);
}
this.sortToggle = {};
if(this.sortInfo){
this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
}
Ext.data.Store.superclass.constructor.call(this);
if(this.storeId || this.id){
Ext.StoreMgr.register(this);
}
if(this.inlineData){
this.loadData(this.inlineData);
delete this.inlineData;
}else if(this.autoLoad){
this.load.defer(10, this, [
typeof this.autoLoad == 'object' ?
this.autoLoad : undefined]);
}
第一个语句中主要就是一个relayEvents,意为延迟事件,这个延迟不是时间延迟哦。它是将当前对像的某些事件处理函数作为另一个对象的处理函数,同者共享,事实上,它的作用就是利用另一对象的事件来触发本对象的事件,从而引发事件处理函数的执行(说得太拗口了吧)。
那个inlineData上面讲了的,现在应验了,不多讲。从这儿可以看出,如果已从config中传过来数据,那么以直接传的数据为准,如果没有直接传数据,而是通过url,且autoLoad为true,这时就在构造函数中加载数据且完全数据的封装。
重点代码至此讲了一半,另一半就是load、loadRecords了。
7. Ext.data.JsonReader篇一
嘿,别看关键就在这儿,事实上,它的代码很少的哦。加上注释才219行。研究研究。
有个事要说一下:DataProxy的子类呢,都有一个load来加载数据,DataReader的子类呢,都有一个read来读取数据。
而Ext.data.JsonReader有两个关键函数:read、readRecords。好了。来研究一下。
Ext.data.JsonReader = function(meta, recordType){
meta = meta || {};
Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
};
这是构造函数。简单。meta是数据格式定义,recordType是记录类型。其中recordType可以是一个定义记录的数组,也可以不传,而把记录的各个字段的定义放到meta中的fields字段中。且看它对父类构造函数的调用:
Ext.data.DataReader = function(meta, recordType){
this.meta = meta;
this.recordType = Ext.isArray(recordType) ?
Ext.data.Record.create(recordType) : recordType;
};
Ext.data.DataReader.prototype = { };
这下全明白了吧。recordType可以是记录类型,可以是字段定义数组,还可以不传。
所以,构造函数就是定义两个属性:meta、recordType。这两东西后面有用。
这个meta、recordType组成如何?这个必须说明,不然,这个类也就没法用了。
meta:
totalProperty json数据中,保存总记录数的属性
successProperty json数据中,保存是否返回成功的属性名
root json数据中,保存记录集的属性的属性名
id json数据中,记录中主键所对应的列的属性名
recordType:
这个东西,事实上要去看Ext.data.Record的create函数的文档,我且把它翻译一下,如下:
create( [Array o] ) : function
创建包含指定字段结构的继承自Ext.data.Record的类。静态方法。
参数:
o : Array
一个定义记录结构的字段信息数组。每个数组元素包含name,其他可选的有:mapping、type。通过它们,可以让Ext.data.Reader从一个数据对象中获取各字段的值。每个字段定义对象都可能包含如下属性:
name : String
在记录中标志一个字段的名字。它通常用于引用指定字段,例如,在定义Ext.grid.ColumnModel的dataIndex属性时,要传过去的。
mapping : String
当在Ext.data.Reader中创建记录时,如何将json对象中指定属性值映射到此字段。
type : String
字段的类型,可能值为:
auto(默认值,没有任何转化)、string、int、float、boolean、date
sortType : Mixed
Ext.data.SortTypes中的一个成员。
sortDir : String
排序方式,"ASC"或者"DESC"。
convert : Function
如果要对这个字段的值进行一些物殊处理,这时需要一个能定制的回调,用它来手工处理值。它的参数如下:
v : Mixed
通过mapping映射找到的值。已从json中取出来的。
rec : Mixed
在json中的,对应于此记录的json对象。
dateFormat : String
用于Date.parseDate函数的格式化字符串。
defaultValue : Mixed
当字段值在原数据中不存在时所取的默认值,默认为空字符串。
用法:
var TopicRecord = Ext.data.Record.create([
{name: 'title', mapping: 'topic_title'},
{name: 'author', mapping: 'username'},
{name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
{name: 'lastPost', mapping: 'post_time', type: 'date'},
{name: 'lastPoster', mapping: 'user2'},
{name: 'excerpt', mapping: 'post_text'}
]);
var myNewRecord = new TopicRecord({
title: 'Do my job please',
author: 'noobie',
totalPosts: 1,
lastPost: new Date(),
lastPoster: 'Animal',
excerpt: 'No way dude!'
});
myStore.add(myNewRecord);
好了,这一篇差不多了,未尽内容放下一篇中了。
8. Ext.data.JsonReader篇二
有了上一篇中所讲内容,一般情况下就可以应付了,不过,JsonReader有一些细节问题,还要细究。待某家一一道来。
构造函数已讲,下面依代码顺序讲解了。
read : function(response){
var json = response.responseText;
var o = eval("("+json+")");
if(!o) {
throw {message: "JsonReader.read: Json object not found"};
}
return this.readRecords(o);
},
这个是整个JsonReader的关键所在了。君可找到Ext.data.HttpProxy中的loadResponse函数,里面有这么一行代码:
result = o.reader.read(response);
可见,是proxy里面调用reader.read方法才得以取出结果集的。这是要表明:read乃JsonReader三军中军之所在。read又调用readRecords,read把json字符串转化为对象然后交给readRecords。这个本无不妥,但是,asp.net中,它的结果有点曲折,结果是放在o.d中,而不能直接从o中取得。所以,事实上应当这么写:this.readRecords(o.d)。这就成了。继续往下面看:
onMetaChange : function(meta, recordType, o){
}
这个函数说是要由store实现的,现在不知道它的用处。还往下看:
simpleAccess: function(obj, subsc) {
return obj[subsc];
},
getJsonAccessor: function(){
var re = /[\[\.]/;
return function(expr) {
try {
return(re.test(expr))
? new Function("obj", "return obj." + expr)
: function(obj){
return obj[expr];
};
} catch(e){}
return Ext.emptyFn;
};
}(),
取一对象的属性有两种方法,前面都已提及:
一、obj.xxxx
二、obj[xxxx]
这两种都行。但是,如果传过来一个对象,已知其对象的引用obj,但是有的只是它的属性名的字符串,这时就可以用第二种方法取出,但是,如属性名中含[],那么就不大方便了,又或者是属性又带属性,这事也只能用第一种方法。这两个函数正是为事而来。且看那getJsonAccessor,着实巧妙,函数返回一函数,这不是巧妙之处,这个我以前就见识了,关键在于new Function("obj","return "obj."+expr)。多么巧妙啊。此之中巧,不足以言语道哉。
这下面就是真正的好戏了,看一看readRecords函数。
this.jsonData = o;
if(o.metaData){
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
定义一个jsonData属性以保存原始json对象。然后如果传过的json对象中就有metaData。那么,就用它自带的meta来取代JsonReader构造函数中所传入的meta。以原来自带的为主。这个功能方档未曾提及,但我辈不可不察也。
var s = this.meta, Record = this.recordType,
f = Record.prototype.fields, fi = f.items, fl = f.length;
有人不理解了,为什么非得这样呢?这是节省带宽啊。如果这些东西以后多说现几次,那么每个用户都要多下载一些东西,成千上万人能节省多少啊。
if (!this.ef) {
if(s.totalProperty) {
this.getTotal = this.getJsonAccessor(s.totalProperty);
}
if(s.successProperty) {
this.getSuccess = this.getJsonAccessor(s.successProperty);
}
this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
if (s.id) {
var g = this.getJsonAccessor(s.id);
this.getId = function(rec) {
var r = g(rec);
return (r === undefined || r === "") ? null : r;
};
} else {
this.getId = function(){return null;};
}
this.ef = [];
for(var i = 0; i < fl; i++){
f = fi[i];
var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
this.ef[i] = this.getJsonAccessor(map);
}
}
因为要根据meta.id、meta.root。这两值都是字符串,这就要用到前面定义的getJsonAccessor函数了。这儿正是来生成几个取json对象中属性的函数,如:getTotal、getSuccess、getRoot、getId、ef数组,一个ef数组就解决了属性映射的问题,真是漂亮。
var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
if(s.totalProperty){
var v = parseInt(this.getTotal(o), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
if(s.successProperty){
var v = this.getSuccess(o);
if(v === false || v === 'false'){
success = false;
}
}
这儿是求totalRecords、success。有一事要注意:其中:
c = root.length, totalRecords = c
这上c后面要用来循环的,而totalRecords是要返回的,而后,又求了totalRecords,这个意思是:如果结果中没有totalProperty这一属性,那么就自动求取,如果存在,则以定义的totalProperty为主,由此可见,totalProperty是可有可无的。这个问题文档不曾见之。诸位可无忧矣。
var records = [];
for(var i = 0; i < c; i++){
var n = root[i];
var values = {};
var id = this.getId(n);
for(var j = 0; j < fl; j++){
f = fi[j];
var v = this.ef[j](n);
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, n);
}
var record = new Record(values, id);
record.json = n;
records[i] = record;
}
return {
success : success,
records : records,
totalRecords : totalRecords
};
这是剩余的代码了,由for(var i = 0; i < c; i++)可知,循环的时候还是用root.length的。而不是totalProperty。这个要分清,事实上,totalProperty只是直接返回罢了,未做任何改动。里面就转化成Record了。其中,这个ef数组用得巧妙。类型转化用了convert。这个东西前文已讲,不足道哉。
var record = new Record(values, id);
id=this.getId(n),可见啦,id并非前文所说的主键,它只是一个用来做客户端唯一编号的东西,如对此有疑,可见于Ext.data.Record类。
record.json = n,json这个属性我在Ext.data.Record类中并未曾得见,诸君注意了,这个东西也许会有用。另外,readRecords返回的不只是一个records数组,而是一个json对象,包含success、records、totalRecords。
至此,JsonReader源代码分析完毕,呵呵,因为这个类代码量较少,故讲得详细。
9. Ext.data.HttpProxy篇
关于Ext.data中各个类的关系图我在前面已经做了一个,不用多言。其实啊。关于数据的显示,一般要经历三个流程:DataProxy-->DataReader-->Store。当然,三个部分都得是具体的类,这三个是抽象类。
如果按照一般性的理解,那么应当先从Proxy开始了。
出人意料: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继承了。如此而己,看代码就看晴晰了。再看一看HttpProxy,它的代码也就一百来行。比起其他类来说,真是小巫见大巫了。
先为Ext.data.HttpProxy给个描述吧:从一个Ext.data.Connection中读取数据到一个数据对象、从Ext.data.DataProxy继承的类。这个类不能跨站出数据,记住了。
此类构函数的文档中说:
HttpProxy( Object conn )
conn是一个connection对象或者是一个传给Ext.Ajax.request的options。如果传给它的是一个options,那么,将使用Ext.Ajax.request获取数据。
这个地方要注意一下。
下面来讲一下load函数,HttpProxy的一切精髓皆在于此。HttpProxy唯一的一个公开的函数。
load( Object params, Ext.data.DataReader reader, Function callback, Object scope, Object arg ) : void
从一个配置好的Ext.data.Connection中读取数据,它通过传递过来的实现自Ext.data.DataReader的对象来读取数据放到一个Ext.data.Records中。并且,在callback中处理这个结果数据。
参数:
params : Object
用于Ext.data.connection.request的options中的params。
reader : Ext.data.DataReader
被用来转化数据的。把数据转化成Ext.data.Records的形式。
callback : Function
用于处理最终结果的回调,当HttpProxy取得connection中的数据,然后交给reader转化了数据后,所得结果集就会交给callback。它的参数如下:
object result
一个记录集对象。
object arg
就是load函数中传过来的arg。
boolean success
是否请求数据成功。
scope : Object
用于callback的scope。
arg : Object
用于callback的arg。
本来看文档没看出明堂来,一结合代码就明白了。原来callback就是用来处理数据的。如果正常的话,这个callback应当是由store来提供吧。它实现这个接口,然后把数据从HttpProxy中接手过来。然后就后就得包装成store了。这还只是我的猜测,具体情况就要看store的代码了。
现在,一切都明显了,取数据是connection的事,不用我们费心了,转换数据成记录集,这个是reader的事,也不用我们费心了。HttpProxy的作用事实就是二者的外观类。现在就要研究一下Ext.data.JsonReader了。
10. Ext.data.Connection篇一
ExtJs之所以能异步请求数据,全依赖于Ext.data.Connection。而Ext.Ajax只不过是Ext.data.Connection的一个实例罢了。当然Ext.Ajax比Ext.data.Connection多了一个函数:serializeForm(form),这个函数的作用是把一个表单里面的表单元素序列化。结果形式为:name1=value1&name2=value2……不过,如果是我的话,一般不会用这个东西,因为平常都是传json数据的,当然,如果不是请求WebService,而是请求aspx页面,那么这个东西还是有点用的。
先把它的官方文档翻译一下吧。
全 称:Ext.data.Connection
命名空间:Ext.data
定义 于:Connection.js
类 名:Connection
子 类:Ajax
父 类:Observable
这个类封装了到页面所在主机的连接,允许通过一个配置好的URL来请求数据,也可以临时在请求时传递一个URL。
通过这个类获得的请求都是异步的,并且马上返回,调用request后,它并不马上返回数据,要处理数据,要在调用request时传入的options对象中,配置callback或者是success、failure。这三个是回调函数。其区别将在下文具体交待。当然,你也可以使用Connection的事件处理来做一些事情。
注意:如果你是要上传文件,你的回调、事件处理函数将不会获得通常意义上的response对象。上传通过IFrame来捕获,所以就没有XMLHttpRequest了。这时,response还是被创建,不过,它的responseText等于IFrame的document.innerHTML,responseXML等于IFrame的document中的xml数据。当然,这个前提是它们存在的时候。
这意味着必面回一个合法的XML或HTML document。如果返回的是JSON数据,那么建议你把数据放到<textarea>标记中,返回时通过正则表达式从responseText中取出来了。如果返回的是XML数据,建议放到CDATA里面,通过标准DOM方法从responseXMl中取得数据。
Options:
autoAbort : Boolean
取消当前请求,不管当前请求是不是存在。默认值为false。
defaultHeaders : Object
默认头部,每个HTTP请求分成两部:头部、数据。数据就是post的部分,头部包含了请求的一些基本属性,此对象定义了用当前connection对象发起的请求的默认头部,默认值为undefined。
disableCaching : Boolean
是否为GET请求加入一个唯一标志的参数缓存。
extraParams : Object
一般情况下,加在encodeURL(params)后面。默认值为:undefined。
method : String
就是http请求的method属性。默认情况下是未定义的(undefined);如果没有设置,但是调用request时设了params,那么将使用POST方式,否则使用GET。
timeout : Number
请求超时,默认值为:30000。单位是millisecond(毫秒?)。
url : String
用此connection对象发起的请求的默认URL。默认值为:undefined。
无公共属性
公共函数(只讲connection自身的,不包括从Observable中继承来的):
Connection( Object config )
构造函数,没有悬念。
abort( [Number transactionId] ) : void
取消指定id的请求,如果没有指定则取消当前请求。
isLoading( [Number transactionId] ) : Boolean
判断指定id的请求是不是正在请求中(?)。
request( [Object options] ) : Number
发送一个HTTP请求到远程主机上。
重点:Ajax服务请求都是异步的,并且这个请求将在response(响应)返回之前返回,也就是说,你绝对无法通过此函数来直接返回数据,你得通过定义回调函数来处理返回的数据。
参数:
options : Object
一个可能包含下面属性的对象:
url : String (Optional)
请求对应的URL,默认值是connection的options配置的那个url。
params : Object/String/Function (Optional)
用于提供url后面的请求参数(俗称查询字符串),可以是json对象,可以是直接的字符串,可以是一个函数。
method : String (Optional)
此http请求的method。默认值为connection的options中配置的method。如果没有设置它,那么就要看params是否设了,如果设了就以POST方式请求,如果没有就以GET方式请求,注意:method名是大小敏感的,必须全面大写。
callback : Function (Optional)
无论请求成功还是失败它都被执行,其参数如下:
options : Object
不用说了。
success : Boolean
是否请求成功了。
response : Object
一个包含响应数据的XMLHttpRequest对象。
success : Function (Optional)
请求成功时执行的回调。它的参数如下:
response : Object
一个包含响应数据的XMLHttpRequest对象。
options : Object
不用说了。
failure : Function (Optional)
请求失败时执行的回调。它的参数如下:
response : Object
一个包含响应数据的XMLHttpRequest对象。
options : Object
不用说了。
scope : Object (Optional)
回调函数执行时所使用的scope。
form : Object/String (Optional)
将用于构造查询字符串的form的引用或id。
isUpload : Boolean (Optional)
当前请求是否是在上传文件(通常是自动检测的)。
文件上传不是通过通常的Ajax技术实现,它们通过在form提交时动态插入一个iframe,返回时又移除这个iframe来实现,一通的英文,就是说响应数据是直接交给浏览器的,这时,就有点能理解为什么要用iframe了。因为它返回的东西会被浏览器直接插入到document对象下面,直给放当前页,那么页面当前内容将消失。所以,只有放一个iframe中了。且这个iframe得隐藏起来。
如果返回结果是json,那么头部要设一下content-type:text/html。
headers : Object (Optional)
请求的头部。
xmlData : Object (Optional)
如果有它,那么params就不会起作用。
jsonData : Object/String (Optional)
如果有它,那么params就不会起作用。
disableCaching : Boolean (Optional)
为真时为Get请求创建一个param缓存。
这个options对象也可以包含其他你需要用于回调的属性,大伙都晓得,这个options最后回被传给回调函数的,所以,也可以加入自己想要的东西。
返回值:
一个请求的id。它用于取消请求。。
事件:
beforerequest : ( Connection conn, Object options )
在请求发生之前触发。
requestcomplete : ( Connection conn, Object response, Object options )
请求结束时触发。
requestexception : ( Connection conn, Object response, Object options )
当http请求处于错误状态时触发。
11. Ext.data.Connection篇二
上一篇主要是扎扎实实地翻译了一下Ext.data.Connection的官文档。尽管网上有位大侠也搞了个中文文档,但是,有不少遗漏的地方。这篇主要是研究一下文档中有些语焉不详的地方,这些问题只能透过研究代码来解释了。
一、Ext.data.Connection是否有依赖的模块
有。它建立在一个适配器类:Ext.lib.Ajax的基础之上,有人看了Ext.js的代码,发现,Ext貌似没有什么底层适配器,事实上,是有的,Ext.lib.Ajax提供了对XMLHttpRequest对象的底层的封装(我直接用ext-base.js)。
二、在options中哪些东西会被编码到url后面
params、extraParams、form。
三、url参数与jsonData、xmlData的关系
这是个非常重大的问题,且见Connection的代码:
if((method == 'GET' || o.xmlData || o.jsonData) && p){
url += (url.indexOf('?') != -1 ? '&' : '?') + p;
p = '';
}
看这三行代码,觉得实在讲不清啦。但是,至少一件事是明白的:如果定义了xmlData、jsonData,且又定义了params/extraParams/form,那么并不会造成参数无用。还是照样传过去了的。
至于xmlData与jsonData的优先级关系,这个要看Ext.lib.Ajax的源码了。源码如下:
if(options.xmlData){
if (!hs || !hs['Content-Type']){
this.initHeader('Content-Type', 'text/xml', false);
}
method = (method ? method : (options.method ? options.method : 'POST'));
data = options.xmlData;
}else if(options.jsonData){
if (!hs || !hs['Content-Type']){
this.initHeader('Content-Type', 'application/json', false);
}
method = (method ? method : (options.method ? options.method : 'POST'));
data = typeof options.jsonData == 'object' ? Ext.encode(options.jsonData) : options.jsonData;
}
可见,如果同时定义了xmlData和jsonData,那么将按发送xmlData中的数据,jsonData中的数据被忽略。
四、那个disableCaching倒底有什么鸟用?
貌似是否使用缓存的意思?文档让人郁闷,且见代码:
if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
}
原来是加个时间参数。拜托了。搞得我们一头的雾水啊。
至此,关于Ext.data.Connection的相关问题都差不多扫清,它的使用例子,前面的“通信篇”中有代码。可以参见。
12. Ext.Updater篇一
上几篇中老老实实地把Ext.data.Connection翻了个遍。这是基础。我记得Ext.Element有一个方法:
load( String/Function url, [String/Object params], [Function callback], [Boolean discardUrl] ) : Ext.Element
直接调用Ext.Updater.update方法处理(它们使用一样的参数)
参数:
url : String/Function
用于请求的url字符串或能返回url的函数。
params : String/Object
放到url后面的查询参数
callback : Function
当请求完毕时执行的回调
discardUrl : Boolean
默认情况下,每执行一次update,defaultUrl属性就会被改成上一次使用过的url,如果为真,则这一次除外,不用保存到defaultUrl。
这个函数在前面没有讲过,事实上它是很多问题的基础,因为一切都建立在Ext.Element基础之上。它的源代码为:
load : function(){
var um = this.getUpdater();
um.update.apply(um, arguments);
return this;
}
超简单。Element有一个方法getUpdater,用于获得一个Ext.Updater类的实例。然后调用它的update方法。所以,一切的关键在Ext.Updater。下面是getUpdater的源代码:
getUpdater : function(){
if(!this.updateManager){
this.updateManager = new Ext.Updater(this);
}
return this.updateManager;
}
看Ext.Updater代码去也。
上面对于load的作用还没有说明。这儿正经地说一下:用于从一个ajax请求中获取数据并更新到此元素中。
Ext.Updater的主要功能有:
一、基于Ext.Ajax请求数据
二、能定时发送请求,也就是说能定时更新某一元素
三、能在更新时显示一个表示正在加载中的“指示器”字符串。
四、提供一个接口用于自定义数据显示:Ext.Updater.BasicRenderer。
功能还是蛮强悍的。
这是初步介绍,下一篇将将Ext.Updater的官方文档翻译一下。
13. Ext.Updater篇二
全 称:Ext.Updater
命名空间:Ext
定义 于:UpdateManager.js
类 名:Updater
父 类:Observable
为Element对象提供Ajax式的更新能力。Updater能用于更新Element一次或者使用startAutoRefresh让Element具备定时更新的能力。
用法:
//从一个Ext.Element对象获得Updater的引用
var el = Ext.get("foo");
var mgr = el.getUpdater();
mgr.update({
url: "http://myserver.com/index.php",
params: {
param1: "foo",
param2: "bar"
}
});
...
mgr.formUpdate("myFormId", "http://myserver.com/index.php");
//或者直接通过Updater构造函数来创建
var mgr = new Ext.Updater("myElementId");
mgr.startAutoRefresh(60, "http://myserver.com/index.php");
mgr.on("update", myFcnNeedsToKnow);
//从element对象的简捷调用方式
Ext.get("foo").load({
url: "bar.php",
scripts: true,
params: "param1=foo¶m2=bar",
text: "Loading Foo..."
});
总结上一面共计有四种更新方法:
updater.update({……});
updater.formUpdate(formname,url);
updater.startAutoRefresh(second,url);
Element.load({……});
公共属性:
defaultUrl : String
保存updater上一次更新时使用的url。
disableCaching : Boolean
是否在url后面上一个唯一标志的参数(当前时间,见Ext.data.Connection),默认值为:Ext.Updater.defaults.disableCaching.
el : Ext.Element
updater使用的element。
formUpdateDelegate : Function
相当于dotnet中的delegate。在别的地方定义,到这儿来调用。回调啦。内部使用方法如下:myUpdater.formUpdateDelegate.createCallback(arg1, arg2)
indicatorText : String
指示器文本(正在加载的时候),默认值为:Ext.Updater.defaults.indicatorText。
loadScripts : Boolean
输出的时候是不是加过脚本(?),默认值为:Ext.Updater.defaults.loadScripts。
refreshDelegate : Function
用于refresh()内的委托,scope使用this。内部使用方法如下:myUpdater.refreshDelegate.createCallback(arg1, arg2)。
renderer : Object
Updater的呈现器(默认值为:Ext.Updater.BasicRenderer)
showLoadIndicator : String
是否在加载过程中显示指示器文本,默认值为:Ext.Updater.defaults.showLoadIndicator。文档有误,应当是boolean类型。
sslBlankUrl : String
空页面url,用于SSL文件上传。默认值为:Ext.Updater.defaults.sslBlankUrl。
timeout : Number
请求超时。单位是秒。默认值为:Ext.Updater.defaults.timeout。
transaction : Object
当前事务对象,如果没有当前事务则为null。
updateDelegate : Function
用于更新(update())的委托。内部使用方式为:myUpdater.updateDelegate.createCallback(arg1, arg2)
公共方法:
Updater( Mixed el, [Boolean forceNew] )
直接创建一个新的Updater对象。
Updater.updateElement( Mixed el, String url, [String/Object params], [Object options] ) : void
不赞成. 一个静态方法. 反对用此函数取代el.load({url:'foo.php', ...})
用法:Ext.Updater.updateElement("my-div", "stuff.php");
abort() : void
取消当前正在执行的事务。
formUpdate( String/HTMLElement form, [String url], [Boolean reset], [Function callback] ) : void
执行一个异步form post。用返回的响应数据更新element。如果form有一个属性:enctype="multipart/form-data",它表示这是上传文件,将使用this.sslBlankUrl来阻止IE安全警告。
参数:
form : String/HTMLElement
form的id或者是element。
url : String
用于form.action。即提交的网址。
reset : Boolean
是否在更新完后重置表单。
callback : Function
当事务完毕后执和,它有如下参数:
el : Ext.Element
正在执行更新的元素
success : Boolean
是否更新成功。
response : XMLHttpRequest
响应结果。。
getEl() : Ext.Element
获得要更新的元素。
getRenderer() : void
取得当前内容呈现器。到Ext.Updater.BasicRenderer.render看更多的细节。
isAutoRefreshing() : void
是否是定时更新。。
isUpdating() : Boolean
是否处于正在更新中。
refresh( [Function callback] ) : void
用上一次更新的地址(defaultUrl)再次更新一下。如果没有就马上返回。
callback : Function
更新完毕后调用。
setDefaultUrl( String/Function defaultUrl ) : void
设置defaultUrl。
setRenderer( Object renderer ) : void
设置呈现器。
showLoading() : void
显示指示器。
startAutoRefresh( Number interval, [String/Object/Function url], [String/Object params], [Function callback], [Boolean refreshNow] ) : void
把这个元素设置为自动更新。通过使用stopAutoRefresh来停止自动更新。
stopAutoRefresh() : void
停止自动更新。
update( Object options ) : void
发起一次异步请求,使用请求的响应结果来更新元素内容。
注意:由于异步请求的一般是远程主机,所以元素不会在此函数返回时更新。要处理返回的数据,请使用回调或事件。
参数:
options : Object
一个包含如下属性的配置对象。
url : String/Function
请求所需要的url或能返回url的函数。
method : String
Post或者是GET。全为大写。
params : String/Object/Function
见Ext.data.Connection中的options.params的说明。
scripts : Boolean
当响应数据中包含<script>……</script>,即包含脚本或脚本引用时,是否提取并执行。为真则执行。默认值为:Ext.Updater.defaults.loadScripts。如果这个属性在options中设置了,那么回调将在此script执行完后再执行。
callback : Function
当响应结果已返回时调用,它有如下参数:
el : Ext.Element
正在更新的元素的引用。
success : Boolean
是否更新成功。
response : XMLHttpRequest
包含响应数据的XMLHttpRequest。
options : Object
传给update方法的options。
scope : Object
回调使用的scope。
discardUrl : Boolean
是否抛弃当前更新的url,不保存到defaultUrls。
timeout : Number
超时设置,单位为秒。默认值为:Ext.Updater.defaults.timeout。
text : String
这个text与indicatorText的区别在于,请见代码:
this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";
nocache : Boolean
用于Ext.data.Connection.disableCaching。
示例代码:
um.update({
url: "your-url.php",
params: {param1: "foo", param2: "bar"}, // or a URL encoded string
callback: yourFunction,
scope: yourObject, //(optional scope)
discardUrl: true,
nocache: true,
text: "Loading...",
timeout: 60,
scripts: false // Save time by avoiding RegExp execution.
});
公共事件:
beforeupdate : ( Ext.Element el, String/Object/Function url, String/Object params )
在更新之前触发。。
failure : ( Ext.Element el, Object oResponseObject )
更新失败时触发。
update : ( Ext.Element el, Object oResponseObject )
更新成功时触发。
14. JSON序列化篇
ExtJs有一个类:Ext.util.JSON,它提供两个函数:encode、decode。用于序列化和反序列化,功能蛮强大的,也差不多了,但是,在WebService中序列化DataTable、DataSet时,遇到麻烦。这个问题只有自己解决了。
其实这个问题简单的很,没什么大不了的。这儿有一篇文章有代码!就是循环做事嘛。但是,我觉得,如果要用于ExtJs的话,这个代码还不够。因为JsonReader好像还需要一些其他的东西。所以呢,代码还是要修正一下的。
上一篇中,用到了:Ext.util.JSON.decode。事实上,这个函数有简写方式的:Ext.decode。事实上Ext类中两个关JSON序列化的函数:Ext.encode、Ext.decode。以方便使用。
Ext.data.JsonReader需要三个东西:id(主键)、root(记录集的引用)、记录数。为此,我修改了上面的代码,得验证通过的代码如下:
public static class Json
{
public static string toJson(DataTable dt)
{
StringBuilder JsonString = new StringBuilder();
//Exception Handling
if (dt != null && dt.Rows.Count > 0)
{
JsonString.Append("{ ");
JsonString.Append("\"count\":" + dt.Rows.Count + ",");
JsonString.Append("\"rows\":[ ");
for (int i = 0; i < dt.Rows.Count; i++)
{
JsonString.Append("{ ");
for (int j = 0; j < dt.Columns.Count; j++)
{
if (j < dt.Columns.Count - 1)
{
JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\",");
}
else if (j == dt.Columns.Count - 1)
{
JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\"");
}
}
/**//*end Of String*/
if (i == dt.Rows.Count - 1)
{
JsonString.Append("} ");
}
else
{
JsonString.Append("}, ");
}
}
JsonString.Append("]}");
return JsonString.ToString();
}
else
{
return null;
}
}
}
实验所得结果如下:
这下子就很明显了。在客户端反序列化的方法如下:
Ext.decode(Ext.decode(response.responseText).d)
一般的情况下应当不会传DataSet吧。实在要传DataSet也容易。调用上面的就行了。写个toJson(DataSet ds),ok了。
15. 通信篇
javascript通过XHR调用WebService。两个问题必须解决:
一、如何传值给WebService,有什么格式要求没有?
二、如何接收从WebService传过来的结果?
此二者我辈不能不察也。asp.net的WebService默认是序列化成json格式的,所以,我们在客户端传值时,最好用json传,反映到ExtJs中。就是Ext.Ajax.request({url:'xxxx/method',jsonData:{paramName:value,……},method:'post',success:function(response,options){……}})。这是在ExtJs中进行异步请求的通式。红色部分就是要传的值了。要注意的是,paramName必须与服务器端的那些参数名相同,不然,WebService怎么晓得你传过去的值是给哪个参数的呢?这是个约定。
下面来研究一下传值的情况:
一、如果传、收数值类型、整型、数组类型
这个毫无悬念,只要调用Ext.util.JSON.decode(response.responseText).d就可以取出来。如果是数组,那么很简单:Ext.util.JSON.decode(response.responseText).d[x]。
二、如果传、收的是日期类型
传过去没什么问题,但是收过来的时候就麻烦了。我试了好久才研究出来。像上面通过:Ext.util.JSON.decode(response.responseText).d得到的是一个字符串,这个字符串的结构一般是:/Date(1212756402000)/。那个数值据说是UTC时间,我把它取出来传到Date里面来构造日期,结果得到了一个1970的某日,郁闷,事实上应当是2008年6月5日才对。突然,我灵光一闪,asp.net为什么要在数值外面加个Date()呢,写了如下表达式,结果成功了:
eval("new "+eval("/Date(1212756402000)/").source)
结果为:Fri Jun 06 2008 20:46:42 GMT+0800
正确无误了。eval真是一个好东西啊。
三、如果传、收的是集合
对于客户端来说,集合有两种形式,呵呵,这个是在总结JavaScript哦:
1.数组array[x]
2.对象成员object.xxxx或object[xxxx]
传过去很简单,没有悬念。无论WebService中的参数类型为数组还是List<xxxx>。对应在这边都是数组。如果是Dictionary<X,Y>。那么它对应的就是:object了(上面的第二种情况)。
还是给个代码出来吧,不然,说服力还是不够的:
Ext.get("btnList").on("click",function(){
var arr=new Array();
for(var i=1;i<=10;i++) arr.push(i);
Ext.Ajax.request({url:'MyService.asmx/fun5',
jsonData:{list:arr},
method:'post',
success:function(response,options){
var result=Ext.util.JSON.decode(response.responseText);
}});
});
服务器端WebService中的方法为:
[WebMethod]
public List<string> fun5(List<int> list)
{
List<string> list2 = new List<string>();
foreach (int i in list)
list2.Add("值为:" + i);
return list2;
}
最后result.d的值为:
["值为:1", "值为:2", "值为:3", "值为:4", "值为:5", "值为:6", "值为:7", "值为:8", "值为:9", "值为:10"]
上面要注意的是:list这个原始参数是不能修改原有值,但是,能在原有值的基础上增加成员。这个问题比较奇怪,我试着修改原有成员,结果,错误希奇古怪。什么“Ext is not defined”。事实上Ext不可能没被定义的。
四、如果传、收的值为一个自定义类的引用时
这是个普遍性的问题,广泛存在着。本人定义了一个简单的类来作实验:
public class Cat
{
public Cat() { }
public string Name { get; set; }
public string Desc { get; set; }
public int Price { get; set; }
public int Weight { get; set; }
}
客户端JavaScript代码:
function Cat(){
this.Name='';
this.Desc='';
this.Price=50;
this.Weight=1;
}
Ext.get("btnCat").on("click",function(){
var cat=new Cat();
cat.Name="加菲猫";
cat.Weight=12;
cat.Price=100;
Ext.Ajax.request({url:'MyService.asmx/fun6',
jsonData:{cat:cat},
method:'post',
success:function(response,options){
var result=Ext.util.JSON.decode(response.responseText);
}});
});
服务器端WebService里面的方法:
[WebMethod]
public Cat fun6(Cat cat)
{
cat.Desc = cat.Desc + "加个随机数字吧:"+(new Random()).Next(1,20);
return cat;
}
实验结果result.d的值为:
注意:这个结果多了个东西:__type。这是asp.net的webservice在序列化返回值时加上去的。这个成员在post到服务器时并没有。
这个中间有个关键,那就是在客户端也要用JavaScript定义一个Cat类.当然,也可以不定义.这个问题留待各位去研究一下.
五、传、收DataTable对象
尽管我现在搞N层结构,不用传DataTable了。但是,相信许多兄弟还要直接来传它。这是个经典问题。
不好意思啊。我实验了,结果response.responseText里面返回了一大通的错误信息,说不能把DataTable序列化。这下子没有什么直接的办法的,除非自己写个类来序列化DataTable。这个需要研究一下了。
六、要调用的WebService的方法没有参数,但是有返回值时
这个时候要小心了,要注意几个问题:
1.在Ext.Ajax.request({……})中不要加jsonData这个成员了。也不要写成:jsonData:{}。这会引发服务器端序列化错误。
2.在Ext.Ajax.request({……})中加上一条:headers:{'Content-Type':'application/json; charset=utf-8'},否则,返回值是xml,而不是json字符串了。
好了,这个通信问题言尽于此,差不多了。
16. extJs 2.0学习笔记(Ajax篇)
一听到Ajax,我与大家一样,如雷贯耳,都说XXX Ajax框架,事实上,这一部分内容在ExtJs中是基础中的基础,就那个样。这儿主要是讨论一些资料、书本都不会涉及的领域。这些东西平常只能由自己摸索的。
在此话题之先,先解决一个问题,现在用asp.net的人多了,但是,用asp.net ajax并不爽,但是asp.net ajax能直接调用webservice,看起来很眼谗,在extJs 2.1之前,是没有办法的,但是在2.1时,就能直接调用asp.net的webservice了。这个我实验证明了。详情点此处参见!
我总结一下用ExtJs访问asp.net service的要点:
一、Url的写法:asmx地址+/+方法名
二、method:post(也可以是get,这样,传参数就得用params,用post的话呢就用jsonData)
三、success:function(response,options),其中response.responseText如果是一个json字符串,那么就要转化,如果只返回一个结果,那么,所得的json对象默认有一个d成员,结果就在它里面。
上面这个问题其实是主客交流的一种最简单的情况。然而,这些其实都不是重点,真正的关键如下:
一、客户端序列化为json字符串后传过去,服务器端怎样取值
二、服务器端把各种数据类型的数据是序列化成何种格式的,客户端如何取。
这两个问题正是我将要研究的。内容将比较多,留在下文了。
17. extJs 2.0学习笔记(Ext.data序论篇)
昨天就说过了,ExtJs的UI部分不会花什么时间了,是时候来研究一下Ext如何发送json数据,如何解析数据,
目录
1. ExtJs 结构树 2
2. 对ExtJs的态度 3
3. Ext.form概述 4
4. Ext.TabPanel篇 5
5. Function扩展篇 7
6. Ext.data.Store篇 10
7. Ext.data.JsonReader篇一 12
8. Ext.data.JsonReader篇二 15
9. Ext.data.HttpProxy篇 19
10. Ext.data.Connection篇一 20
11. Ext.data.Connection篇二 24
12. Ext.Updater篇一 26
13. Ext.Updater篇二 27
14. JSON序列化篇 33
15. 通信篇 35
16. extJs 2.0学习笔记(Ajax篇) 38
17. extJs 2.0学习笔记(Ext.data序论篇) 39
18. extJs 2.0学习笔记(Ext.Panel终结篇) 40
19. extJs 2.0学习笔记(事件注册总结篇) 45
20. extJs 2.0学习笔记(Ext.Panel篇一) 48
21. extJs 2.0学习笔记(Ext.Panel篇二) 50
22. extJs 2.0学习笔记(Ext.Panel篇三) 59
23. extJs 2.0学习笔记(Ext.Panel篇四) 62
24. extJs 2.0学习笔记(组件总论篇) 66
25. extJs 2.0学习笔记(Ext.Element API总结) 69
26. extJs 2.0学习笔记(Element.js篇) 73
27. extJs 2.0学习笔记(DomHelper.js篇) 76
28. extJs 2.0学习笔记(ext.js篇) 77
1. ExtJs 结构树
2. 对ExtJs的态度
extjs的确是个好东西,但是,它的优点也就是它的缺点:
• 加载页面慢
• 时间一长,浏览器占内存就会疯长
• 服务器端功能极度削弱,除了数据库操作外,几乎所有功能都到了客户端,还得用javascript来写。
• 功能全到了客户端,而客户端语言javascript的编写还是有许多的麻烦,真正精通它的人极少。
• javascript对于大部分人来说,不易调试
• 大量存在的javascript代码难以维护
• 开发速度极慢。
• extjs本身还存在一些问题
正是因为有这么多的问题,老板们都得掂量一下了。用它倒底值不值。当然,这儿也得说一下它的优点:
• 因为一切都是javascript搞定,所以,界面上的问题再也不像以前一样让人郁闷了,客户端对界面的的操作取得极大的便利,而不像以前一样,服务器端生成n多垃圾代码,以前的时代就彷佛隔靴搔痒,服务器端企图布置好一切。现在不同了,客户端用一个Ext.Ajax.Request请求数据方便,然后,显示出来也容易。
• 又回到了c/s时代。c/s让人神往啊。web该死的无状态让人郁闷
• 学习extjs的一个极大的好处,所有当前web开发界面上的需求都可以在这儿找到答案。通过研究它的代码,我们可以开发出自己的ajax框架来,可以写出适合于自己的widgets来。而不用背着extjs那个大乌龟壳。
我认为,不宜用extjs来开发整个应用,但是,在极为需要的地方用一用,还是蛮好的,整个站点都用它那就麻烦了。现在我对于选择ajax框架有了一点心得。
不要使用extjs来开发,但是,一定要学习、研究它,研究它之后才会晓得,我们写代码应当这么写才优美、才合适。研究了它后就应当选一款轻量型的框架了。然后自己写组件。用以取代:Ext.Window、Ext.TabPanel、Ext.Panel这些好东西。
研究了extjs,我敢说:一览众山小啊!什么prototype、dojo、jQuery之类,就容易多了。
真正要用的ajax框架,我看,倒不如选择prototype,它是个轻量型,我觉得,一个ajax,只要封装了三个东西就行了:
一、Element。把dom元素要封装一下,加入动画、求取、设置各种参数值的功能
二、XMLHttpRequest,要把它封装一下,这个所有框架都做了
三、把事件机制要封装一下,最好像extjs一样,xxx.on('click',function(){});就成了。
有了这三个就差不多了,那些什么window、tabs,网上多的是代码,搞些下来改篇改篇就成了。
关于prototype,我找到了它的中文文档(1.5的),1.5的大小是93.7k,事实上,这个大小还可以缩小,可以使用工具去掉多余的空格,差不多了。
3. Ext.form概述
Ext.form中封装了是输入组件。input、textArea、frameSet、form等元素都被包装起来了。我刚才发了点时间对它的类图分析了一下,用StartUML做了图如下:
Ext.form中的组件太多,实在不大
4. Ext.TabPanel篇
Ext.TabPanel这个东西是最常用的组件之一,它继承自Ext.Panel。看了一个下午的源代码,对它的一些基本原理有所了解了。
下面要讲一些问题,这些问题绝对是本人独门秘笈,非入室弟子不传。哈哈哈。
一、组件的组成:
因为继承自Ext.Panel,所以,它也是由header、tbar、body、bbar、footer这几个部分构成,有人问:TabPanel的面板标签是在哪儿呢(就是你点击换页的东西)?它默认是放在header中的。但是,如果设置了:tabPosition的话就不一定了,tabPosition可取两个值:top、bottom。所以,标签可以是放在下面,但是,Ext目前还不支技放在左边、右边。
那么,不同的标签是用什么元素来组织的呢?用ul。一页对应一个li。li的id的取值有规律哦,它的取值公式如下:tabpanel.id+tabpanel.idDelimiter+面板的id。正是因为有了这个规律,才能根据点击的标签而找到对应的面板。这个问题是一大主题,在下面讲。
这是面板的标签,下面的面板呢?简单!!!一个Ext.Panel对应一个面板,注意:这儿的面板是没有header的,如果你想tab.items.get(1).header,在这儿,header===undefined。为什么为面板定义的title会对应到标签中去呢?这个是TabPanel的特意处理的。至于换页效果是怎么出来的?CardLayout。这下组件的大概结构都清楚了。还有不明白,自己new Ext.TabPanel({……})一个,然后在FireBug下面去查看dom结构,就一清二楚了。
二、处理标签的事件
为什么要研究这个问题?有需求的,如何在鼠标移到标签上时就显示对应的面板呢?默认情况下,TabPanel是不支持这个功能的,但是,这个功能有时是需要的。这儿有点小技巧。
看Ext.TabPanel源代码中关于标签的事件处理:
this.strip.on('mousedown', this.onStripMouseDown, this);
this.strip.on('click', this.onStripClick, this);
this.strip.on('contextmenu', this.onStripContextMenu, this);
if(this.enableTabScroll){
this.strip.on('mousewheel', this.onWheel, this);
}
这段代码写在initEvents函数中,先解释一下,this.strip是指头部放标签的那个ul元素,相信,98%的读者会想,要注册事件也应当是为li元素注册,怎么会统统注册到ul这个父容器上面呢?原理就是事件冒泡。关于事件传递的原理,本人在下一文中有详细的实验、明确的结论,不再赘言。
ul元素捕获了事件,怎样在事件处理函数中得知倒底是哪个li发生了事件呢?Ext写了个函数:findTargets。详情请见如下代码:
findTargets : function(e){
var item = null;
var itemEl = e.getTarget('li', this.strip);
if(itemEl){
item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
if(item.disabled){
return {
close : null,
item : null,
el : null
};
}
}
return {
close : e.getTarget('.x-tab-strip-close', this.strip),
item : item,
el : itemEl
};
},
// private
onStripMouseDown : function(e){
e.preventDefault();
if(e.button != 0){
return;
}
var t = this.findTargets(e);
if(t.close){
this.remove(t.item);
return;
}
if(t.item && t.item != this.activeTab){
this.setActiveTab(t.item);
}
},
一切的关键就在于li元素的id的命名规则,从中取出对应的面板的id,这样就能getComponent,从而获得对应的面板引用,再setActiveTab就办成了。至于getTarget这个是EventObject中封装的函数,作用是在事件传播路径上查找满足指定选择条件的元素。这个函数的详情见它的源码。
到了这里,之前所讲的鼠标悬停问题只要依照方面方法解决就是了,切记,不要处理mouseout事件,不然,事情就麻烦了,详情见我以前写过的关于mouseover事件的一篇文章。
5. Function扩展篇
ExtJs对JavaScript的内建对象进行了扩展,对什么Object、Date、Array、Function、String的扩展,扩展方法想必诸位都烂熟于心了:用prototype的办法。这一篇讲一讲Function扩展的精妙之处,之所以突然研究这个问题,是因为我在研究Ext.data.Store的源代码时,看到一行代码:
this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
当初,我在研究Ext.js中的代码时,对于Function的几个扩展想不透、看不明,今日大悟。且见扩展的源代码:
createDelegate : function(obj, args, appendArgs){
var method = this;
return function() {
var callArgs = args || arguments;
if(appendArgs === true){
callArgs = Array.prototype.slice.call(arguments, 0);
callArgs = callArgs.concat(args);
}else if(typeof appendArgs == "number"){
callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
var applyArgs = [appendArgs, 0].concat(args); // create method call params
Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
}
return method.apply(obj || window, callArgs);
};
},
createDelegate函数的作用是,为指定函数创建一个回调函数,注意是创建一个新的函数返回,它返回的是一个新函数。我以前一直不明白,为什么要这么做,就像上面红色的那行代码,相信大伙与我一样,都在想,为什么不是写成这样:
this.reader.onMetaChange=this.onMetaChange;
不是应当这样写的吗?如果用过dotnet,那么委托一定是晓得了,javascript中的函数跟c#的委托一样,有很相近的意义,为什么c#中能这样写,JavaScript中不能这样写呢?
一切都因为this,this这个东西见风使舵,像上面onMetaChange这函数,实际调用时是在reader中,那么如果onMetaChange中使用了this关键字,那么,this是指向reader的,而不是指向onMetaChange的定义环境所对应的this。而事实上,我们往往想把这个this指向函数的定义环境,这也正是回调的最招人喜欢的地方,然而,因为this的问题,回调就不能像上面那样直接赋值。还得做些手脚,得让函数调用时scope为当前定义环境。
改变一个函数执行的scope,熟翻JavaScript的兄弟一定晓得要用:call、apply。至此,createDelegate的产生背景、作用都作了个交代。
createDelegate(this),调用时,一般直接传个this就行了,当真是妙啊。事实上,我上面讲的一通道理清楚了,这个函数的代码就没有秘密可言了。关键就是一个this。我现在感叹,你对JavaScript的造诣与你对this的领悟层次成正比。
既然讲了createDelegate,其他几个扩展函数一并讲了。
createCallback : function(/*args...*/){
// make args available, in function below
var args = arguments;
var method = this;
return function() {
return method.apply(window, args);
};
}
也是创建调用者的回调,不过,回调函数的scope为window。相当于createDelegate(window)。没什么讲的。
defer : function(millis, obj, args, appendArgs){
var fn = this.createDelegate(obj, args, appendArgs);
if(millis){
return setTimeout(fn, millis);
}
fn();
return 0;
},
此函数调用一次就让函数延迟调用一次。对setTimeout的封装罢了。如果没有定义延时参数,那么就马上执行。这个函数也没有技术性可言。
createSequence : function(fcn, scope){
if(typeof fcn != "function"){
return this;
}
var method = this;
return function() {
var retval = method.apply(this || window, arguments);
fcn.apply(scope || this || window, arguments);
return retval;
};
},
这个函数就有点意思了,刚开始研究ext.js的时候还没有看明白,它的作用是在返回一个函数,此函数先调用“调用函数”,后调用传递进来的函数。这句话可能还没说清,见示例如下:
function A(){alert("第一个执行!");return 1;}
function B(){alert("第二个执行!");return 2;}
function C(){alert("第三个执行!");return 3;}
var D=A.createSequence(B).createSequence(C);
var result=D();
上面代码产生的效果是:
第一弹出框显示:第一个执行!
第二弹出框显示:第二个执行!
第三弹出框显示:第三个执行!
result的值为:3。
这下子诸位都明白了吧。用过dotnet的知道,委托变量有这种类似的功能。就是累加执行的效果。
createInterceptor : function(fcn, scope){
if(typeof fcn != "function"){
return this;
}
var method = this;
return function() {
fcn.target = this;
fcn.method = method;
if(fcn.apply(scope || this || window, arguments) === false){
return;
}
return method.apply(this || window, arguments);
};
}
这个函数也有点意思,有创意,它返回被调用函数的回调,这个回调是条件执行的,执行条件是createInterceptor传入的那个函数返回真。示例代码如下:
function A(){}
var B=A.createInterceptor(function(i){return i>0;});
B(1),则A被执行,如果调用B(-1),A则不被执行。B的作用就是如果传入的第一个参数的值大于0时A才被执行,否则不执行。
相当于原有函数的功能不变,只是加个执行条件。这个想法着实巧妙。这一招现在想来,也可以用到c#中。
6. Ext.data.Store篇
Ext.data.Store,这个东西是JavaScript版的DataTable啊。貌似其他Ajax框架都没有这个玩意啊。可见啦,Ext是真的打算把b/s开发重新变成c/s开发啊。哈哈哈。便宜我等了。待某细研之。
Store类提供对记录集(Record)的包装,通过前面的研究可知,DataProxy取数据(url或数组或xml或json),DataReader用于从不规范的数据取出并格式化指定结构的记录集。记录的结构由Record.create创建。
DataProxy通过对Connection的调用取得数据(Response)后,在回调中调用DataReader的read函数,从而把response中的数据解析成记录集,这个记录集将再以回调参数的形式传出来,store实现这个回调,并把里面的Recodrd[]取出来,放到data这个成员中。store.data是一个MixedCollection对象,MixedCollection作什么用的前面也讲过,它本质就是一个容器,ExtJs确实很好,连容器类都写了。
有了store.data,数据进了这儿,就好办了,store调用MixedCollection的功能,实现了一些通用的函数,如取指定成员、查询、遍历、事务等等,这些都不足道。什么提交修改、取消修改的功能却是根源于Record。Record类自身就封装了这个功能,Store中只是再次封装罢了,这个原理也很简单。看代码即知。
上面讲的是通用原理,是大概,下面拣紧要的代码说一下。
它定义了构造函数,继承自Ext.Observable。第一行代码就是个重点:
this.data = new Ext.util.MixedCollection(false);
这是定义data,所有记录都将保存在它里面。
this.baseParams = {};
// private
this.paramNames = {
"start" : "start",
"limit" : "limit",
"sort" : "sort",
"dir" : "dir"
};
baseParams将在调用HttpProxy时用到,它将作为params附加到url末尾。这个东西没有悬念。至于paramsNames用于保存参数名,start、limit应当用于分页,sort、dir用于排序,不过,我看了通篇的代码,发现,Store本身不提供任何其他分页、排序功能的实现,还是得依靠服务器端的。只不过,这儿提供一种统一的方式罢了。
if(config && config.data){
this.inlineData = config.data;
delete config.data;
}
意思是说,如果创建store时,设了config,且config.data存在,那么,将直接从config.data中loadData。构造函数后面一点就有。inlineData这个属性没活多久就被delete了。
if(this.url && !this.proxy){
this.proxy = new Ext.data.HttpProxy({url: this.url});
}
if(this.reader){ // reader passed
if(!this.recordType){
this.recordType = this.reader.recordType;
}
if(this.reader.onMetaChange){
this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
}
}
if(this.recordType){
this.fields = this.recordType.prototype.fields;
}
就是根据config中的情况,创建成员:proxy,reader,recordType,onMetaChange。这了这四个,就好方便在下面定义的load中加载数据并完全记录集的封装。说出来一文不值。
this.modified = [];
这个东西用于保存那些有修改过的记录的旧值。之所以能取消修改,正是源于此啊。
关于addEvents那个语句,就没必要讲了,大伙都懂。
if(this.proxy){
this.relayEvents(this.proxy, ["loadexception"]);
}
this.sortToggle = {};
if(this.sortInfo){
this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
}
Ext.data.Store.superclass.constructor.call(this);
if(this.storeId || this.id){
Ext.StoreMgr.register(this);
}
if(this.inlineData){
this.loadData(this.inlineData);
delete this.inlineData;
}else if(this.autoLoad){
this.load.defer(10, this, [
typeof this.autoLoad == 'object' ?
this.autoLoad : undefined]);
}
第一个语句中主要就是一个relayEvents,意为延迟事件,这个延迟不是时间延迟哦。它是将当前对像的某些事件处理函数作为另一个对象的处理函数,同者共享,事实上,它的作用就是利用另一对象的事件来触发本对象的事件,从而引发事件处理函数的执行(说得太拗口了吧)。
那个inlineData上面讲了的,现在应验了,不多讲。从这儿可以看出,如果已从config中传过来数据,那么以直接传的数据为准,如果没有直接传数据,而是通过url,且autoLoad为true,这时就在构造函数中加载数据且完全数据的封装。
重点代码至此讲了一半,另一半就是load、loadRecords了。
7. Ext.data.JsonReader篇一
嘿,别看关键就在这儿,事实上,它的代码很少的哦。加上注释才219行。研究研究。
有个事要说一下:DataProxy的子类呢,都有一个load来加载数据,DataReader的子类呢,都有一个read来读取数据。
而Ext.data.JsonReader有两个关键函数:read、readRecords。好了。来研究一下。
Ext.data.JsonReader = function(meta, recordType){
meta = meta || {};
Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
};
这是构造函数。简单。meta是数据格式定义,recordType是记录类型。其中recordType可以是一个定义记录的数组,也可以不传,而把记录的各个字段的定义放到meta中的fields字段中。且看它对父类构造函数的调用:
Ext.data.DataReader = function(meta, recordType){
this.meta = meta;
this.recordType = Ext.isArray(recordType) ?
Ext.data.Record.create(recordType) : recordType;
};
Ext.data.DataReader.prototype = { };
这下全明白了吧。recordType可以是记录类型,可以是字段定义数组,还可以不传。
所以,构造函数就是定义两个属性:meta、recordType。这两东西后面有用。
这个meta、recordType组成如何?这个必须说明,不然,这个类也就没法用了。
meta:
totalProperty json数据中,保存总记录数的属性
successProperty json数据中,保存是否返回成功的属性名
root json数据中,保存记录集的属性的属性名
id json数据中,记录中主键所对应的列的属性名
recordType:
这个东西,事实上要去看Ext.data.Record的create函数的文档,我且把它翻译一下,如下:
create( [Array o] ) : function
创建包含指定字段结构的继承自Ext.data.Record的类。静态方法。
参数:
o : Array
一个定义记录结构的字段信息数组。每个数组元素包含name,其他可选的有:mapping、type。通过它们,可以让Ext.data.Reader从一个数据对象中获取各字段的值。每个字段定义对象都可能包含如下属性:
name : String
在记录中标志一个字段的名字。它通常用于引用指定字段,例如,在定义Ext.grid.ColumnModel的dataIndex属性时,要传过去的。
mapping : String
当在Ext.data.Reader中创建记录时,如何将json对象中指定属性值映射到此字段。
type : String
字段的类型,可能值为:
auto(默认值,没有任何转化)、string、int、float、boolean、date
sortType : Mixed
Ext.data.SortTypes中的一个成员。
sortDir : String
排序方式,"ASC"或者"DESC"。
convert : Function
如果要对这个字段的值进行一些物殊处理,这时需要一个能定制的回调,用它来手工处理值。它的参数如下:
v : Mixed
通过mapping映射找到的值。已从json中取出来的。
rec : Mixed
在json中的,对应于此记录的json对象。
dateFormat : String
用于Date.parseDate函数的格式化字符串。
defaultValue : Mixed
当字段值在原数据中不存在时所取的默认值,默认为空字符串。
用法:
var TopicRecord = Ext.data.Record.create([
{name: 'title', mapping: 'topic_title'},
{name: 'author', mapping: 'username'},
{name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
{name: 'lastPost', mapping: 'post_time', type: 'date'},
{name: 'lastPoster', mapping: 'user2'},
{name: 'excerpt', mapping: 'post_text'}
]);
var myNewRecord = new TopicRecord({
title: 'Do my job please',
author: 'noobie',
totalPosts: 1,
lastPost: new Date(),
lastPoster: 'Animal',
excerpt: 'No way dude!'
});
myStore.add(myNewRecord);
好了,这一篇差不多了,未尽内容放下一篇中了。
8. Ext.data.JsonReader篇二
有了上一篇中所讲内容,一般情况下就可以应付了,不过,JsonReader有一些细节问题,还要细究。待某家一一道来。
构造函数已讲,下面依代码顺序讲解了。
read : function(response){
var json = response.responseText;
var o = eval("("+json+")");
if(!o) {
throw {message: "JsonReader.read: Json object not found"};
}
return this.readRecords(o);
},
这个是整个JsonReader的关键所在了。君可找到Ext.data.HttpProxy中的loadResponse函数,里面有这么一行代码:
result = o.reader.read(response);
可见,是proxy里面调用reader.read方法才得以取出结果集的。这是要表明:read乃JsonReader三军中军之所在。read又调用readRecords,read把json字符串转化为对象然后交给readRecords。这个本无不妥,但是,asp.net中,它的结果有点曲折,结果是放在o.d中,而不能直接从o中取得。所以,事实上应当这么写:this.readRecords(o.d)。这就成了。继续往下面看:
onMetaChange : function(meta, recordType, o){
}
这个函数说是要由store实现的,现在不知道它的用处。还往下看:
simpleAccess: function(obj, subsc) {
return obj[subsc];
},
getJsonAccessor: function(){
var re = /[\[\.]/;
return function(expr) {
try {
return(re.test(expr))
? new Function("obj", "return obj." + expr)
: function(obj){
return obj[expr];
};
} catch(e){}
return Ext.emptyFn;
};
}(),
取一对象的属性有两种方法,前面都已提及:
一、obj.xxxx
二、obj[xxxx]
这两种都行。但是,如果传过来一个对象,已知其对象的引用obj,但是有的只是它的属性名的字符串,这时就可以用第二种方法取出,但是,如属性名中含[],那么就不大方便了,又或者是属性又带属性,这事也只能用第一种方法。这两个函数正是为事而来。且看那getJsonAccessor,着实巧妙,函数返回一函数,这不是巧妙之处,这个我以前就见识了,关键在于new Function("obj","return "obj."+expr)。多么巧妙啊。此之中巧,不足以言语道哉。
这下面就是真正的好戏了,看一看readRecords函数。
this.jsonData = o;
if(o.metaData){
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
定义一个jsonData属性以保存原始json对象。然后如果传过的json对象中就有metaData。那么,就用它自带的meta来取代JsonReader构造函数中所传入的meta。以原来自带的为主。这个功能方档未曾提及,但我辈不可不察也。
var s = this.meta, Record = this.recordType,
f = Record.prototype.fields, fi = f.items, fl = f.length;
有人不理解了,为什么非得这样呢?这是节省带宽啊。如果这些东西以后多说现几次,那么每个用户都要多下载一些东西,成千上万人能节省多少啊。
if (!this.ef) {
if(s.totalProperty) {
this.getTotal = this.getJsonAccessor(s.totalProperty);
}
if(s.successProperty) {
this.getSuccess = this.getJsonAccessor(s.successProperty);
}
this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
if (s.id) {
var g = this.getJsonAccessor(s.id);
this.getId = function(rec) {
var r = g(rec);
return (r === undefined || r === "") ? null : r;
};
} else {
this.getId = function(){return null;};
}
this.ef = [];
for(var i = 0; i < fl; i++){
f = fi[i];
var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
this.ef[i] = this.getJsonAccessor(map);
}
}
因为要根据meta.id、meta.root。这两值都是字符串,这就要用到前面定义的getJsonAccessor函数了。这儿正是来生成几个取json对象中属性的函数,如:getTotal、getSuccess、getRoot、getId、ef数组,一个ef数组就解决了属性映射的问题,真是漂亮。
var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
if(s.totalProperty){
var v = parseInt(this.getTotal(o), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
if(s.successProperty){
var v = this.getSuccess(o);
if(v === false || v === 'false'){
success = false;
}
}
这儿是求totalRecords、success。有一事要注意:其中:
c = root.length, totalRecords = c
这上c后面要用来循环的,而totalRecords是要返回的,而后,又求了totalRecords,这个意思是:如果结果中没有totalProperty这一属性,那么就自动求取,如果存在,则以定义的totalProperty为主,由此可见,totalProperty是可有可无的。这个问题文档不曾见之。诸位可无忧矣。
var records = [];
for(var i = 0; i < c; i++){
var n = root[i];
var values = {};
var id = this.getId(n);
for(var j = 0; j < fl; j++){
f = fi[j];
var v = this.ef[j](n);
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, n);
}
var record = new Record(values, id);
record.json = n;
records[i] = record;
}
return {
success : success,
records : records,
totalRecords : totalRecords
};
这是剩余的代码了,由for(var i = 0; i < c; i++)可知,循环的时候还是用root.length的。而不是totalProperty。这个要分清,事实上,totalProperty只是直接返回罢了,未做任何改动。里面就转化成Record了。其中,这个ef数组用得巧妙。类型转化用了convert。这个东西前文已讲,不足道哉。
var record = new Record(values, id);
id=this.getId(n),可见啦,id并非前文所说的主键,它只是一个用来做客户端唯一编号的东西,如对此有疑,可见于Ext.data.Record类。
record.json = n,json这个属性我在Ext.data.Record类中并未曾得见,诸君注意了,这个东西也许会有用。另外,readRecords返回的不只是一个records数组,而是一个json对象,包含success、records、totalRecords。
至此,JsonReader源代码分析完毕,呵呵,因为这个类代码量较少,故讲得详细。
9. Ext.data.HttpProxy篇
关于Ext.data中各个类的关系图我在前面已经做了一个,不用多言。其实啊。关于数据的显示,一般要经历三个流程:DataProxy-->DataReader-->Store。当然,三个部分都得是具体的类,这三个是抽象类。
如果按照一般性的理解,那么应当先从Proxy开始了。
出人意料: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继承了。如此而己,看代码就看晴晰了。再看一看HttpProxy,它的代码也就一百来行。比起其他类来说,真是小巫见大巫了。
先为Ext.data.HttpProxy给个描述吧:从一个Ext.data.Connection中读取数据到一个数据对象、从Ext.data.DataProxy继承的类。这个类不能跨站出数据,记住了。
此类构函数的文档中说:
HttpProxy( Object conn )
conn是一个connection对象或者是一个传给Ext.Ajax.request的options。如果传给它的是一个options,那么,将使用Ext.Ajax.request获取数据。
这个地方要注意一下。
下面来讲一下load函数,HttpProxy的一切精髓皆在于此。HttpProxy唯一的一个公开的函数。
load( Object params, Ext.data.DataReader reader, Function callback, Object scope, Object arg ) : void
从一个配置好的Ext.data.Connection中读取数据,它通过传递过来的实现自Ext.data.DataReader的对象来读取数据放到一个Ext.data.Records中。并且,在callback中处理这个结果数据。
参数:
params : Object
用于Ext.data.connection.request的options中的params。
reader : Ext.data.DataReader
被用来转化数据的。把数据转化成Ext.data.Records的形式。
callback : Function
用于处理最终结果的回调,当HttpProxy取得connection中的数据,然后交给reader转化了数据后,所得结果集就会交给callback。它的参数如下:
object result
一个记录集对象。
object arg
就是load函数中传过来的arg。
boolean success
是否请求数据成功。
scope : Object
用于callback的scope。
arg : Object
用于callback的arg。
本来看文档没看出明堂来,一结合代码就明白了。原来callback就是用来处理数据的。如果正常的话,这个callback应当是由store来提供吧。它实现这个接口,然后把数据从HttpProxy中接手过来。然后就后就得包装成store了。这还只是我的猜测,具体情况就要看store的代码了。
现在,一切都明显了,取数据是connection的事,不用我们费心了,转换数据成记录集,这个是reader的事,也不用我们费心了。HttpProxy的作用事实就是二者的外观类。现在就要研究一下Ext.data.JsonReader了。
10. Ext.data.Connection篇一
ExtJs之所以能异步请求数据,全依赖于Ext.data.Connection。而Ext.Ajax只不过是Ext.data.Connection的一个实例罢了。当然Ext.Ajax比Ext.data.Connection多了一个函数:serializeForm(form),这个函数的作用是把一个表单里面的表单元素序列化。结果形式为:name1=value1&name2=value2……不过,如果是我的话,一般不会用这个东西,因为平常都是传json数据的,当然,如果不是请求WebService,而是请求aspx页面,那么这个东西还是有点用的。
先把它的官方文档翻译一下吧。
全 称:Ext.data.Connection
命名空间:Ext.data
定义 于:Connection.js
类 名:Connection
子 类:Ajax
父 类:Observable
这个类封装了到页面所在主机的连接,允许通过一个配置好的URL来请求数据,也可以临时在请求时传递一个URL。
通过这个类获得的请求都是异步的,并且马上返回,调用request后,它并不马上返回数据,要处理数据,要在调用request时传入的options对象中,配置callback或者是success、failure。这三个是回调函数。其区别将在下文具体交待。当然,你也可以使用Connection的事件处理来做一些事情。
注意:如果你是要上传文件,你的回调、事件处理函数将不会获得通常意义上的response对象。上传通过IFrame来捕获,所以就没有XMLHttpRequest了。这时,response还是被创建,不过,它的responseText等于IFrame的document.innerHTML,responseXML等于IFrame的document中的xml数据。当然,这个前提是它们存在的时候。
这意味着必面回一个合法的XML或HTML document。如果返回的是JSON数据,那么建议你把数据放到<textarea>标记中,返回时通过正则表达式从responseText中取出来了。如果返回的是XML数据,建议放到CDATA里面,通过标准DOM方法从responseXMl中取得数据。
Options:
autoAbort : Boolean
取消当前请求,不管当前请求是不是存在。默认值为false。
defaultHeaders : Object
默认头部,每个HTTP请求分成两部:头部、数据。数据就是post的部分,头部包含了请求的一些基本属性,此对象定义了用当前connection对象发起的请求的默认头部,默认值为undefined。
disableCaching : Boolean
是否为GET请求加入一个唯一标志的参数缓存。
extraParams : Object
一般情况下,加在encodeURL(params)后面。默认值为:undefined。
method : String
就是http请求的method属性。默认情况下是未定义的(undefined);如果没有设置,但是调用request时设了params,那么将使用POST方式,否则使用GET。
timeout : Number
请求超时,默认值为:30000。单位是millisecond(毫秒?)。
url : String
用此connection对象发起的请求的默认URL。默认值为:undefined。
无公共属性
公共函数(只讲connection自身的,不包括从Observable中继承来的):
Connection( Object config )
构造函数,没有悬念。
abort( [Number transactionId] ) : void
取消指定id的请求,如果没有指定则取消当前请求。
isLoading( [Number transactionId] ) : Boolean
判断指定id的请求是不是正在请求中(?)。
request( [Object options] ) : Number
发送一个HTTP请求到远程主机上。
重点:Ajax服务请求都是异步的,并且这个请求将在response(响应)返回之前返回,也就是说,你绝对无法通过此函数来直接返回数据,你得通过定义回调函数来处理返回的数据。
参数:
options : Object
一个可能包含下面属性的对象:
url : String (Optional)
请求对应的URL,默认值是connection的options配置的那个url。
params : Object/String/Function (Optional)
用于提供url后面的请求参数(俗称查询字符串),可以是json对象,可以是直接的字符串,可以是一个函数。
method : String (Optional)
此http请求的method。默认值为connection的options中配置的method。如果没有设置它,那么就要看params是否设了,如果设了就以POST方式请求,如果没有就以GET方式请求,注意:method名是大小敏感的,必须全面大写。
callback : Function (Optional)
无论请求成功还是失败它都被执行,其参数如下:
options : Object
不用说了。
success : Boolean
是否请求成功了。
response : Object
一个包含响应数据的XMLHttpRequest对象。
success : Function (Optional)
请求成功时执行的回调。它的参数如下:
response : Object
一个包含响应数据的XMLHttpRequest对象。
options : Object
不用说了。
failure : Function (Optional)
请求失败时执行的回调。它的参数如下:
response : Object
一个包含响应数据的XMLHttpRequest对象。
options : Object
不用说了。
scope : Object (Optional)
回调函数执行时所使用的scope。
form : Object/String (Optional)
将用于构造查询字符串的form的引用或id。
isUpload : Boolean (Optional)
当前请求是否是在上传文件(通常是自动检测的)。
文件上传不是通过通常的Ajax技术实现,它们通过在form提交时动态插入一个iframe,返回时又移除这个iframe来实现,一通的英文,就是说响应数据是直接交给浏览器的,这时,就有点能理解为什么要用iframe了。因为它返回的东西会被浏览器直接插入到document对象下面,直给放当前页,那么页面当前内容将消失。所以,只有放一个iframe中了。且这个iframe得隐藏起来。
如果返回结果是json,那么头部要设一下content-type:text/html。
headers : Object (Optional)
请求的头部。
xmlData : Object (Optional)
如果有它,那么params就不会起作用。
jsonData : Object/String (Optional)
如果有它,那么params就不会起作用。
disableCaching : Boolean (Optional)
为真时为Get请求创建一个param缓存。
这个options对象也可以包含其他你需要用于回调的属性,大伙都晓得,这个options最后回被传给回调函数的,所以,也可以加入自己想要的东西。
返回值:
一个请求的id。它用于取消请求。。
事件:
beforerequest : ( Connection conn, Object options )
在请求发生之前触发。
requestcomplete : ( Connection conn, Object response, Object options )
请求结束时触发。
requestexception : ( Connection conn, Object response, Object options )
当http请求处于错误状态时触发。
11. Ext.data.Connection篇二
上一篇主要是扎扎实实地翻译了一下Ext.data.Connection的官文档。尽管网上有位大侠也搞了个中文文档,但是,有不少遗漏的地方。这篇主要是研究一下文档中有些语焉不详的地方,这些问题只能透过研究代码来解释了。
一、Ext.data.Connection是否有依赖的模块
有。它建立在一个适配器类:Ext.lib.Ajax的基础之上,有人看了Ext.js的代码,发现,Ext貌似没有什么底层适配器,事实上,是有的,Ext.lib.Ajax提供了对XMLHttpRequest对象的底层的封装(我直接用ext-base.js)。
二、在options中哪些东西会被编码到url后面
params、extraParams、form。
三、url参数与jsonData、xmlData的关系
这是个非常重大的问题,且见Connection的代码:
if((method == 'GET' || o.xmlData || o.jsonData) && p){
url += (url.indexOf('?') != -1 ? '&' : '?') + p;
p = '';
}
看这三行代码,觉得实在讲不清啦。但是,至少一件事是明白的:如果定义了xmlData、jsonData,且又定义了params/extraParams/form,那么并不会造成参数无用。还是照样传过去了的。
至于xmlData与jsonData的优先级关系,这个要看Ext.lib.Ajax的源码了。源码如下:
if(options.xmlData){
if (!hs || !hs['Content-Type']){
this.initHeader('Content-Type', 'text/xml', false);
}
method = (method ? method : (options.method ? options.method : 'POST'));
data = options.xmlData;
}else if(options.jsonData){
if (!hs || !hs['Content-Type']){
this.initHeader('Content-Type', 'application/json', false);
}
method = (method ? method : (options.method ? options.method : 'POST'));
data = typeof options.jsonData == 'object' ? Ext.encode(options.jsonData) : options.jsonData;
}
可见,如果同时定义了xmlData和jsonData,那么将按发送xmlData中的数据,jsonData中的数据被忽略。
四、那个disableCaching倒底有什么鸟用?
貌似是否使用缓存的意思?文档让人郁闷,且见代码:
if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
}
原来是加个时间参数。拜托了。搞得我们一头的雾水啊。
至此,关于Ext.data.Connection的相关问题都差不多扫清,它的使用例子,前面的“通信篇”中有代码。可以参见。
12. Ext.Updater篇一
上几篇中老老实实地把Ext.data.Connection翻了个遍。这是基础。我记得Ext.Element有一个方法:
load( String/Function url, [String/Object params], [Function callback], [Boolean discardUrl] ) : Ext.Element
直接调用Ext.Updater.update方法处理(它们使用一样的参数)
参数:
url : String/Function
用于请求的url字符串或能返回url的函数。
params : String/Object
放到url后面的查询参数
callback : Function
当请求完毕时执行的回调
discardUrl : Boolean
默认情况下,每执行一次update,defaultUrl属性就会被改成上一次使用过的url,如果为真,则这一次除外,不用保存到defaultUrl。
这个函数在前面没有讲过,事实上它是很多问题的基础,因为一切都建立在Ext.Element基础之上。它的源代码为:
load : function(){
var um = this.getUpdater();
um.update.apply(um, arguments);
return this;
}
超简单。Element有一个方法getUpdater,用于获得一个Ext.Updater类的实例。然后调用它的update方法。所以,一切的关键在Ext.Updater。下面是getUpdater的源代码:
getUpdater : function(){
if(!this.updateManager){
this.updateManager = new Ext.Updater(this);
}
return this.updateManager;
}
看Ext.Updater代码去也。
上面对于load的作用还没有说明。这儿正经地说一下:用于从一个ajax请求中获取数据并更新到此元素中。
Ext.Updater的主要功能有:
一、基于Ext.Ajax请求数据
二、能定时发送请求,也就是说能定时更新某一元素
三、能在更新时显示一个表示正在加载中的“指示器”字符串。
四、提供一个接口用于自定义数据显示:Ext.Updater.BasicRenderer。
功能还是蛮强悍的。
这是初步介绍,下一篇将将Ext.Updater的官方文档翻译一下。
13. Ext.Updater篇二
全 称:Ext.Updater
命名空间:Ext
定义 于:UpdateManager.js
类 名:Updater
父 类:Observable
为Element对象提供Ajax式的更新能力。Updater能用于更新Element一次或者使用startAutoRefresh让Element具备定时更新的能力。
用法:
//从一个Ext.Element对象获得Updater的引用
var el = Ext.get("foo");
var mgr = el.getUpdater();
mgr.update({
url: "http://myserver.com/index.php",
params: {
param1: "foo",
param2: "bar"
}
});
...
mgr.formUpdate("myFormId", "http://myserver.com/index.php");
//或者直接通过Updater构造函数来创建
var mgr = new Ext.Updater("myElementId");
mgr.startAutoRefresh(60, "http://myserver.com/index.php");
mgr.on("update", myFcnNeedsToKnow);
//从element对象的简捷调用方式
Ext.get("foo").load({
url: "bar.php",
scripts: true,
params: "param1=foo¶m2=bar",
text: "Loading Foo..."
});
总结上一面共计有四种更新方法:
updater.update({……});
updater.formUpdate(formname,url);
updater.startAutoRefresh(second,url);
Element.load({……});
公共属性:
defaultUrl : String
保存updater上一次更新时使用的url。
disableCaching : Boolean
是否在url后面上一个唯一标志的参数(当前时间,见Ext.data.Connection),默认值为:Ext.Updater.defaults.disableCaching.
el : Ext.Element
updater使用的element。
formUpdateDelegate : Function
相当于dotnet中的delegate。在别的地方定义,到这儿来调用。回调啦。内部使用方法如下:myUpdater.formUpdateDelegate.createCallback(arg1, arg2)
indicatorText : String
指示器文本(正在加载的时候),默认值为:Ext.Updater.defaults.indicatorText。
loadScripts : Boolean
输出的时候是不是加过脚本(?),默认值为:Ext.Updater.defaults.loadScripts。
refreshDelegate : Function
用于refresh()内的委托,scope使用this。内部使用方法如下:myUpdater.refreshDelegate.createCallback(arg1, arg2)。
renderer : Object
Updater的呈现器(默认值为:Ext.Updater.BasicRenderer)
showLoadIndicator : String
是否在加载过程中显示指示器文本,默认值为:Ext.Updater.defaults.showLoadIndicator。文档有误,应当是boolean类型。
sslBlankUrl : String
空页面url,用于SSL文件上传。默认值为:Ext.Updater.defaults.sslBlankUrl。
timeout : Number
请求超时。单位是秒。默认值为:Ext.Updater.defaults.timeout。
transaction : Object
当前事务对象,如果没有当前事务则为null。
updateDelegate : Function
用于更新(update())的委托。内部使用方式为:myUpdater.updateDelegate.createCallback(arg1, arg2)
公共方法:
Updater( Mixed el, [Boolean forceNew] )
直接创建一个新的Updater对象。
Updater.updateElement( Mixed el, String url, [String/Object params], [Object options] ) : void
不赞成. 一个静态方法. 反对用此函数取代el.load({url:'foo.php', ...})
用法:Ext.Updater.updateElement("my-div", "stuff.php");
abort() : void
取消当前正在执行的事务。
formUpdate( String/HTMLElement form, [String url], [Boolean reset], [Function callback] ) : void
执行一个异步form post。用返回的响应数据更新element。如果form有一个属性:enctype="multipart/form-data",它表示这是上传文件,将使用this.sslBlankUrl来阻止IE安全警告。
参数:
form : String/HTMLElement
form的id或者是element。
url : String
用于form.action。即提交的网址。
reset : Boolean
是否在更新完后重置表单。
callback : Function
当事务完毕后执和,它有如下参数:
el : Ext.Element
正在执行更新的元素
success : Boolean
是否更新成功。
response : XMLHttpRequest
响应结果。。
getEl() : Ext.Element
获得要更新的元素。
getRenderer() : void
取得当前内容呈现器。到Ext.Updater.BasicRenderer.render看更多的细节。
isAutoRefreshing() : void
是否是定时更新。。
isUpdating() : Boolean
是否处于正在更新中。
refresh( [Function callback] ) : void
用上一次更新的地址(defaultUrl)再次更新一下。如果没有就马上返回。
callback : Function
更新完毕后调用。
setDefaultUrl( String/Function defaultUrl ) : void
设置defaultUrl。
setRenderer( Object renderer ) : void
设置呈现器。
showLoading() : void
显示指示器。
startAutoRefresh( Number interval, [String/Object/Function url], [String/Object params], [Function callback], [Boolean refreshNow] ) : void
把这个元素设置为自动更新。通过使用stopAutoRefresh来停止自动更新。
stopAutoRefresh() : void
停止自动更新。
update( Object options ) : void
发起一次异步请求,使用请求的响应结果来更新元素内容。
注意:由于异步请求的一般是远程主机,所以元素不会在此函数返回时更新。要处理返回的数据,请使用回调或事件。
参数:
options : Object
一个包含如下属性的配置对象。
url : String/Function
请求所需要的url或能返回url的函数。
method : String
Post或者是GET。全为大写。
params : String/Object/Function
见Ext.data.Connection中的options.params的说明。
scripts : Boolean
当响应数据中包含<script>……</script>,即包含脚本或脚本引用时,是否提取并执行。为真则执行。默认值为:Ext.Updater.defaults.loadScripts。如果这个属性在options中设置了,那么回调将在此script执行完后再执行。
callback : Function
当响应结果已返回时调用,它有如下参数:
el : Ext.Element
正在更新的元素的引用。
success : Boolean
是否更新成功。
response : XMLHttpRequest
包含响应数据的XMLHttpRequest。
options : Object
传给update方法的options。
scope : Object
回调使用的scope。
discardUrl : Boolean
是否抛弃当前更新的url,不保存到defaultUrls。
timeout : Number
超时设置,单位为秒。默认值为:Ext.Updater.defaults.timeout。
text : String
这个text与indicatorText的区别在于,请见代码:
this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";
nocache : Boolean
用于Ext.data.Connection.disableCaching。
示例代码:
um.update({
url: "your-url.php",
params: {param1: "foo", param2: "bar"}, // or a URL encoded string
callback: yourFunction,
scope: yourObject, //(optional scope)
discardUrl: true,
nocache: true,
text: "Loading...",
timeout: 60,
scripts: false // Save time by avoiding RegExp execution.
});
公共事件:
beforeupdate : ( Ext.Element el, String/Object/Function url, String/Object params )
在更新之前触发。。
failure : ( Ext.Element el, Object oResponseObject )
更新失败时触发。
update : ( Ext.Element el, Object oResponseObject )
更新成功时触发。
14. JSON序列化篇
ExtJs有一个类:Ext.util.JSON,它提供两个函数:encode、decode。用于序列化和反序列化,功能蛮强大的,也差不多了,但是,在WebService中序列化DataTable、DataSet时,遇到麻烦。这个问题只有自己解决了。
其实这个问题简单的很,没什么大不了的。这儿有一篇文章有代码!就是循环做事嘛。但是,我觉得,如果要用于ExtJs的话,这个代码还不够。因为JsonReader好像还需要一些其他的东西。所以呢,代码还是要修正一下的。
上一篇中,用到了:Ext.util.JSON.decode。事实上,这个函数有简写方式的:Ext.decode。事实上Ext类中两个关JSON序列化的函数:Ext.encode、Ext.decode。以方便使用。
Ext.data.JsonReader需要三个东西:id(主键)、root(记录集的引用)、记录数。为此,我修改了上面的代码,得验证通过的代码如下:
public static class Json
{
public static string toJson(DataTable dt)
{
StringBuilder JsonString = new StringBuilder();
//Exception Handling
if (dt != null && dt.Rows.Count > 0)
{
JsonString.Append("{ ");
JsonString.Append("\"count\":" + dt.Rows.Count + ",");
JsonString.Append("\"rows\":[ ");
for (int i = 0; i < dt.Rows.Count; i++)
{
JsonString.Append("{ ");
for (int j = 0; j < dt.Columns.Count; j++)
{
if (j < dt.Columns.Count - 1)
{
JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\",");
}
else if (j == dt.Columns.Count - 1)
{
JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\"");
}
}
/**//*end Of String*/
if (i == dt.Rows.Count - 1)
{
JsonString.Append("} ");
}
else
{
JsonString.Append("}, ");
}
}
JsonString.Append("]}");
return JsonString.ToString();
}
else
{
return null;
}
}
}
实验所得结果如下:
这下子就很明显了。在客户端反序列化的方法如下:
Ext.decode(Ext.decode(response.responseText).d)
一般的情况下应当不会传DataSet吧。实在要传DataSet也容易。调用上面的就行了。写个toJson(DataSet ds),ok了。
15. 通信篇
javascript通过XHR调用WebService。两个问题必须解决:
一、如何传值给WebService,有什么格式要求没有?
二、如何接收从WebService传过来的结果?
此二者我辈不能不察也。asp.net的WebService默认是序列化成json格式的,所以,我们在客户端传值时,最好用json传,反映到ExtJs中。就是Ext.Ajax.request({url:'xxxx/method',jsonData:{paramName:value,……},method:'post',success:function(response,options){……}})。这是在ExtJs中进行异步请求的通式。红色部分就是要传的值了。要注意的是,paramName必须与服务器端的那些参数名相同,不然,WebService怎么晓得你传过去的值是给哪个参数的呢?这是个约定。
下面来研究一下传值的情况:
一、如果传、收数值类型、整型、数组类型
这个毫无悬念,只要调用Ext.util.JSON.decode(response.responseText).d就可以取出来。如果是数组,那么很简单:Ext.util.JSON.decode(response.responseText).d[x]。
二、如果传、收的是日期类型
传过去没什么问题,但是收过来的时候就麻烦了。我试了好久才研究出来。像上面通过:Ext.util.JSON.decode(response.responseText).d得到的是一个字符串,这个字符串的结构一般是:/Date(1212756402000)/。那个数值据说是UTC时间,我把它取出来传到Date里面来构造日期,结果得到了一个1970的某日,郁闷,事实上应当是2008年6月5日才对。突然,我灵光一闪,asp.net为什么要在数值外面加个Date()呢,写了如下表达式,结果成功了:
eval("new "+eval("/Date(1212756402000)/").source)
结果为:Fri Jun 06 2008 20:46:42 GMT+0800
正确无误了。eval真是一个好东西啊。
三、如果传、收的是集合
对于客户端来说,集合有两种形式,呵呵,这个是在总结JavaScript哦:
1.数组array[x]
2.对象成员object.xxxx或object[xxxx]
传过去很简单,没有悬念。无论WebService中的参数类型为数组还是List<xxxx>。对应在这边都是数组。如果是Dictionary<X,Y>。那么它对应的就是:object了(上面的第二种情况)。
还是给个代码出来吧,不然,说服力还是不够的:
Ext.get("btnList").on("click",function(){
var arr=new Array();
for(var i=1;i<=10;i++) arr.push(i);
Ext.Ajax.request({url:'MyService.asmx/fun5',
jsonData:{list:arr},
method:'post',
success:function(response,options){
var result=Ext.util.JSON.decode(response.responseText);
}});
});
服务器端WebService中的方法为:
[WebMethod]
public List<string> fun5(List<int> list)
{
List<string> list2 = new List<string>();
foreach (int i in list)
list2.Add("值为:" + i);
return list2;
}
最后result.d的值为:
["值为:1", "值为:2", "值为:3", "值为:4", "值为:5", "值为:6", "值为:7", "值为:8", "值为:9", "值为:10"]
上面要注意的是:list这个原始参数是不能修改原有值,但是,能在原有值的基础上增加成员。这个问题比较奇怪,我试着修改原有成员,结果,错误希奇古怪。什么“Ext is not defined”。事实上Ext不可能没被定义的。
四、如果传、收的值为一个自定义类的引用时
这是个普遍性的问题,广泛存在着。本人定义了一个简单的类来作实验:
public class Cat
{
public Cat() { }
public string Name { get; set; }
public string Desc { get; set; }
public int Price { get; set; }
public int Weight { get; set; }
}
客户端JavaScript代码:
function Cat(){
this.Name='';
this.Desc='';
this.Price=50;
this.Weight=1;
}
Ext.get("btnCat").on("click",function(){
var cat=new Cat();
cat.Name="加菲猫";
cat.Weight=12;
cat.Price=100;
Ext.Ajax.request({url:'MyService.asmx/fun6',
jsonData:{cat:cat},
method:'post',
success:function(response,options){
var result=Ext.util.JSON.decode(response.responseText);
}});
});
服务器端WebService里面的方法:
[WebMethod]
public Cat fun6(Cat cat)
{
cat.Desc = cat.Desc + "加个随机数字吧:"+(new Random()).Next(1,20);
return cat;
}
实验结果result.d的值为:
注意:这个结果多了个东西:__type。这是asp.net的webservice在序列化返回值时加上去的。这个成员在post到服务器时并没有。
这个中间有个关键,那就是在客户端也要用JavaScript定义一个Cat类.当然,也可以不定义.这个问题留待各位去研究一下.
五、传、收DataTable对象
尽管我现在搞N层结构,不用传DataTable了。但是,相信许多兄弟还要直接来传它。这是个经典问题。
不好意思啊。我实验了,结果response.responseText里面返回了一大通的错误信息,说不能把DataTable序列化。这下子没有什么直接的办法的,除非自己写个类来序列化DataTable。这个需要研究一下了。
六、要调用的WebService的方法没有参数,但是有返回值时
这个时候要小心了,要注意几个问题:
1.在Ext.Ajax.request({……})中不要加jsonData这个成员了。也不要写成:jsonData:{}。这会引发服务器端序列化错误。
2.在Ext.Ajax.request({……})中加上一条:headers:{'Content-Type':'application/json; charset=utf-8'},否则,返回值是xml,而不是json字符串了。
好了,这个通信问题言尽于此,差不多了。
16. extJs 2.0学习笔记(Ajax篇)
一听到Ajax,我与大家一样,如雷贯耳,都说XXX Ajax框架,事实上,这一部分内容在ExtJs中是基础中的基础,就那个样。这儿主要是讨论一些资料、书本都不会涉及的领域。这些东西平常只能由自己摸索的。
在此话题之先,先解决一个问题,现在用asp.net的人多了,但是,用asp.net ajax并不爽,但是asp.net ajax能直接调用webservice,看起来很眼谗,在extJs 2.1之前,是没有办法的,但是在2.1时,就能直接调用asp.net的webservice了。这个我实验证明了。详情点此处参见!
我总结一下用ExtJs访问asp.net service的要点:
一、Url的写法:asmx地址+/+方法名
二、method:post(也可以是get,这样,传参数就得用params,用post的话呢就用jsonData)
三、success:function(response,options),其中response.responseText如果是一个json字符串,那么就要转化,如果只返回一个结果,那么,所得的json对象默认有一个d成员,结果就在它里面。
上面这个问题其实是主客交流的一种最简单的情况。然而,这些其实都不是重点,真正的关键如下:
一、客户端序列化为json字符串后传过去,服务器端怎样取值
二、服务器端把各种数据类型的数据是序列化成何种格式的,客户端如何取。
这两个问题正是我将要研究的。内容将比较多,留在下文了。
17. extJs 2.0学习笔记(Ext.data序论篇)
昨天就说过了,ExtJs的UI部分不会花什么时间了,是时候来研究一下Ext如何发送json数据,如何解析数据,