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。看了一个下午的源代码,对它的一些基本原理有所了解了。

  下面要讲一些问题,详细实例可参看本栏的 技术教程www.gjrencai.com

  一、组件的组成:

  因为继承自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.requestoptions。如果传给它的是一个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,默认值是connectionoptions配置的那个url

    params : Object/String/Function (Optional)
    用于提供url后面的请求参数(俗称查询字符串),可以是json对象,可以是直接的字符串,可以是一个函数。

    method : String (Optional)
    此http请求的method。默认值为connectionoptions中配置的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参数与jsonDataxmlData的关系

  这是个非常重大的问题,且见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
  默认情况下,每执行一次updatedefaultUrl属性就会被改成上一次使用过的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一次或者使用startAutoRefreshElement具备定时更新的能力。

用法:

//从一个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&param2=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.encodeExt.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.xxxxobject[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数据,如何解析数据,如何显示到我们的widgets。如何管理异步请求。这些问题貌似都在Ext.data里面。本人总结了一张UML图。可以得到一个大概关系。

 

  其中,像Observable、DataReader、Record、SortTypes这四个类没有标出父类,是因为它们继承自Object,就省了。这个图还清晰吧。

  由这个图可知,ExtJs的数据处理包括三个部分:proxy、reader、store。至于connection、ajax,这个是对于XHR(XmlHttpRequest)的封装。没什么好讲的。关于XHR对象,本人前面作过一文,点此查阅!至于tree、node这两个类与数据处理根本无关,Record相当于dotnet中的DataRow。表示一行记录。我们通过它来构造自己的记录类。

  关于proxy、reader、store三者之间的关系,网上有少数资料讲到,不过,人云亦云如何求得大道。我打算在解决了Connection、Ajax这两个类后把它给研究透彻。


  这两天极为不爽,加上,越来越明白ExtJs的局限性。把ExtJs真正应用于项目只怕还是有麻烦的。不过,它很值得借鉴。不为别的,只为它优秀的理念、结构、UI。不得不令人佩服。不过,大量的js代码,伴随着需求的不断变化,这些js代码如何有效、方便地维护。这足以让大部分流口水的人掉头就走啊。没想到又是一个虎头蛇尾啊。

18.         extJs 2.0学习笔记(Ext.Panel终结篇)

  怪不得我对Ext.Panel穷追猛打,前面已经写过四篇针对它的文章了。不过。Ext.Panel的问题搞得差不多了。下面我贴出研究代码,及效果图。再解释一下。这下图文结合了。以后自己忘记了,看一下就明白了。

var panel1;

function newPanel1(){
 var config1={title:'
这是标题栏',
     width:300,
     height:300,
     floating:true,
     renderTo:Ext.getBody(),
     draggable:{
       insertProxy: false,
     
       onDrag : function(e){
        var pel = this.proxy.getEl();
        this.x = pel.getLeft(true);
        this.y = pel.getTop(true);
     
        var s = this.panel.getEl().shadow;
        if (s) {
         s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
        }
       },
     
       endDrag : function(e){
        this.panel.setPosition(this.x, this.y);
       }
     },
     tools:[{id:close,handler:function(event, toolEl, panel){panel.hide();}}],
     layout:'border',
     items: [{
       title: '
左边栏',
       
region: 'west',
       split:true,
      
       height: 100,
       width:200,
       minSize: 75,
       maxSize: 250,
       margins: '5 0 5 5'
      },{
       title: '
中间主体部分',
       region:'center',
       margins:'5 5 5 0',
       split:true,

       minSize: 100,
      }],
     tbar:['
请输入关键字:',
    {xtype:'textfield',width:80},'-',
    {text:'有种就点一下',handler:function(){alert("小子,你刚才点了此按钮!");}}],
     collapsible:true,
     shadow:false};
 
 panel1=new Ext.Panel(config1);
 panel1.setPosition(0,0);
 
 var resizer=new Ext.Resizable(panel1.getEl(),{handlers:'all'});
 resizer.on('resize',function(){panel1.updateBox(panel1.getSize());});
}

Ext.onReady(newPanel1);

  效果图如下:

  

  看了上面的效果心里痒了吧。多漂亮啊。上面代码的要点基本上在前面第四篇中讲过了。也有一些事还没有来得及讲。我分门别类的补充一下。

  一、Ext.Panel的组成

  Ext.Panel有五个部分,即:header、body、tbar、bbar、footer。在创建了panel的实例后,这五个对象就能被引用了,它们分别指向panel五个最重要的部分,tbar就是上面的工具栏,bbar就是下面的工具栏,footer就是放buttons的地方,它处于最下面。body就是整个panel的主体部分了。事实上,大伙只要用FireBug去看一下panel的dom结构就会发现,body外面还有一个div。这个div也有一个引用,叫:bwrap。不过,官方API文档中没有提及,想必是不想让大伙去用它,怕以后会有变更。另外,tbar、bbar也是非文档支持的。官方推荐使用:getTopToolbar()、getBottomToolbar(),用它们来获得上下两个工具栏的引用,不过,这种方法只有在render后才有效,有的兄弟使用了它,结果鸟结果都没有。大惑、不解。如果要在render前操作工具栏,还是要用tbar、bbar的。

  header、body、footer在官方文档中都有说明,是Ext.Element类型。不用说了。

  基本上,有了这五个对象我们可以用它们来设置panel的各个部分了。

  二、如臂使指的操作Ext.Panel的每一寸土地

  有了上面五个对象,能搞定一些事情,但是,要说完全控制Ext.Panel这头驴还有所不及啊。这个问题一般人俺是不给他讲的哦。哈哈哈。

  panel最外层的那个div,可以用panel.el或panel.getEl()来获得它的Element引用。很多人不能理解,有了它有个屁用?一个Panel能玩出前面所说的这么多的花样足够了。够用了。然而永远不要小看客户的花花肠子。他们名堂多着呐。有了panel.el,能有什么好处,嘿,晓得Ext.Fx不?Element能用到所有在它里面定义的特效(animations)。而在Panel中,你去看一看官方API文档,没一字提及。也没法控制它搞点特效出来。你哪怕看了Ext.Panel也不会结果的。怎么办,得依靠Element了。我现在庆幸当初把Element的API研究得很彻底。

  特效是一方面,另外,你去看一看Ext.Window的API文档,会发现,它有alignTo、anchorTo的功能了,这可是好东西啊。但是,Ext.Panel没有这个功能。我不用去看Ext.Window的代码就晓得,这肯定是取了panel.el来搞的,Element有alignTo、anchorTo这两个API。例如:如何使Panel居中:panel.el.center()就行了。超强大啊。

  所以啊。有了panel.el这个引用,依托于ExtJs对dom的强大封装结果所得的Element。我们可以做任何事(你能想到的、不能想到的)。

  另外,还有一个东西没有被文档公开:component.container。那指向component.el的父元素(div)。也是个Element引用。尽管我们能用这个Element做事,但是,貌似还没有用到它的时候。机会不多。

  好了,这儿谈到的都是关于控制Panel自身的架子的,说通俗点,架子是衣柜,但是衣柜里面的衣服怎么取出来呢?panel.el已经能很好地控制整个衣柜,但是,衣服还是一件都取不出来啊。有兄弟发火了,不是骗我吗?这就是所谓的控制每一寸土地吗?鉴于这个内容层次问题,把它放下一小节。

  三、取panel这个衣柜里的衣服

  这个问题是我老刘的独门武学,我敢保证晓得这事的人不多。晓得这事的人必定是用FireBug研究过panel的dom结构的人或者是研究过layout的源代码的人。这种人不多啊。大伙都本着能用就行的想法啊。不吹了。还记得Ext.Panel的API文档上面一句话:

  If this Panel is intended to be used as the host of a Layout (See layout then the body Element must not be loaded or changed - it is under the control of the Panel's Layout.

  这是关于body的说明中的一句话,说得好,精辟,它说,如果你想在panel中用布局类,那么你不得以任意方式来修改body。不要想着用panel.body.update(xxxx)来干活了。因为文档说了:it is under the control of the Panel's Layout。它正处在panel的layout的控制之下,嘿,这是我的layout的地盘,你就不要来整蛊了。

  就是说,要么用body,要么就只能用layout。双方是排斥的,这不难理解。我一个小时前,在FireBug写了条命令语句:

  console.dir(panel1);

  这个东西诸位不晓得的话,那意味着你要去研究一下FireBug了。它的作用是把panel1的所有成员都输出到控制台窗口中。我看了输出的结果,发现:panel有一个layout的成员,如图:

 

  可能图看不清,诸位点一下看原图吧。我发现了个有趣的结果:

  layout有两个成员:center、west。

  这儿我用的是border layout。因为这儿我只用到两个区,所以只有center、west。如果五个区都有了,推理得:那就会有五个了:west/east/center/south/north。原来,borderlayout是有五个这样的引用,可供我们来控制这五个区域。

  哈哈哈,有了这五个对象,就相当于衣柜中有五个格子,我的手又能伸进更细致的地方了。

  依此推理(我还没来得及研究layout的源代码)可知,我们是有能力利用layout的提供的一些接口来控制body内部的细节的。

  刚才灵光一闪,API文档中,提到两个类:

  Ext.layout.BorderLayout.Region

  Ext.layout.BorderLayout.SplitRegion

  我在FireBug控制台写下如下实验代码:

  panel1.layout.west instanceof Ext.layout.BorderLayout.SplitRegion

  结果为:true

  嘿,可见,borderLayout是内部是以区域对象来进行控制的。不过,在BorderLayout的官方文档中没有对此提及,Ext.layout.BorderLayout.Region这两个类的文档倒是有的。

  总结经验:layout不仅仅只是设计时布局,也许它提供给我们一些有意思的接口啊,只是,这个可能需要自己去研究、琢磨。

   四、关于BorderLayout的钉子

  再次吹牛,这个问题老实说,在API上面是没有讲到的。就是在定义borderLayout的各个区域时,注意要设几个量:region/split/margins/cmargins。这个用法应当不用讲了,自己瞎琢磨去吧。那个cmargins的用法我还没有摸明白。网上关于这方面的细节资料太少了,一个劲地在那儿打屁。难有实质性内容,真是的。

  五、关于Toolbar的事

  关于工具栏类我现在还没有来得及研究。不过初步用法还是要说一下:

  'xxxxx'相当于{xtype:'tbtext',text:'xxxxx'}

  '-'相当于{xtype:'tbseparator'}

  也是个简写法。

  六、关于resize功能与Ext.Panel的一些冲突

  pinned:true不能设、shadow必须为false。不然冲突起来的效果……描述出来。这个是我个人摸索的结果。不想描述。试一试就出来了。

 

  至此,Ext.Panel总算终结了。我等不及了,得研究一下Ext.data中的东西了。关于UI,一个理,把Element、Component、Observable、BoxComponent好好的研究一把,UI就触类旁通了。

  伯平经验:要研究ExtJs,先把JavaScript+DOM搞得炉火纯青了再说(比欲练此功,挥刀自宫容易多了吧!)

  祝诸君学有所成。

19.         extJs 2.0学习笔记(事件注册总结篇)

本来,我也没把这档子事放在眼里,因为简单,例如:

  Ext.get("elem").on("click",{fn:function(){alert("此元素被单击了!");}};

  这样的代码谁不会写啊。一个on就了结了。但是,今天,我在研究Ext.Panel的tbar时,发现,那现工具栏按钮的事件注册不一样:

  元素注册、组件注册都是:{fn:……}

  工具栏按钮的事件注册:{handler:……}

  嘿,我就在想,为什么Ext的作者就不统一一下呢,都是fn多好。省得我分心了。一不小心准搞错了。还好,不是fn就是handler,凡是工具栏上面的东西注册事件,一般都是用handler,平常组件注册事件统统都用fn。

  关于组件的事件注册,一般都是创建时就定义好,而不是创建后再来on。所以,用extjs写程序,常常看到Ext.onReady里面嵌套了无数层,items里面还有items,items里面还有listeners。然后listeners里面定义事件处理器。这样,代码就比较难看了。有人说:extJs写的代码莫名其妙。呵呵,可见大伙不大喜欢这种写法啊。

  关于事件处理,我把Element的API文档翻译了一下。事实上事件注册都是建立在Element之上的。

  一、元素的事件注册
  on( String eventName, Function fn, [Object scope], [Object options] ) : void
  其中:
    eventName:String
  事件名称
  fn:Function
    事件处理函数,它有三个参数
    evt:EventObject 事件对象
    t:Element    事件发生的目标Element,注意:它将被delegate选项所筛选(很有用)。
    o:Object     addListener函数传入的options对象。

  scope:Object 范围
  options:Object 选项参数
  一个包含了事件配置属性的对象,它可能包括如下属性:
  scope {Object} :它表示事件处理函数的执行范围,即处理函数时面的this的上下文。
  delegate {String} :一个简单的selector,用于过滤target或者找target的子孙。
  stopEvent {Boolean} :为true就停止事件,即停止事件的传播和阻止默认行为。
  preventDefault {Boolean} :阻止默认行为
  stopPropagation {Boolean}:停止事件的传播
  normalized {Boolean} : 为false的话就传递一个浏览器的原装事件对象给函数,而不是Ext.EventObject。
  delay {Number} : 这个值表示事件发生后多少毫秒,事件处理函数才被执行。
  single {Boolean} : 为真的话呢就表示这个事件处理器只执行一次,之后自删除。
  buffer {Number} :它的作用就是执行缓冲,有时候,用户点按钮做死的点,一秒点它几十次,难道让事件处理函数执行几十次吗?其实,用户并不一定是执行多次。如果真的就这样老老实实执行了,很可能就坏事了,做了无用功。
  这个值表示,在事件发生后,事件处理函数将放到Ext.util.DelayedTask中去计划执行,多少毫秒之内,如果再次发生同一事件,那么,这一事件将覆盖原来的事件。只执行后面那一次,当然,那个缓冲时间也在后一次时被刷新。


  组合Options中的选项
  在下面的例子中,on这种快捷的方式比冗长的addListener好用的多了。两者是等价的。使用Options作参数,它能组合多种不同的事件处理器:
  一个普通的,能延时执行的,只执行一次的,能自动停止事件的,还有一个自定义参数(forumId)在options对象,这个Options对象是合法的。代码如下:

el.on('click', this.onClick, this, {
    single: true,
    delay: 100,
    stopEvent : true,
    forumId: 4
});

  一次注册多个事件
  这个方法也允许只传一个config,但是一个config中包含多个事件处理信息。代码如下:
el.on({
    'click' : {
        fn: this.onClick,
        scope: this,
        delay: 100
    },
    'mouseover' : {
        fn: this.onMouseOver,
        scope: this
    },
    'mouseout' : {
        fn: this.onMouseOut,
        scope: this
    }
});
或者是以下简捷语法:
el.on({
    'click' : this.onClick,
    'mouseover' : this.onMouseOver,
    'mouseout' : this.onMouseOut,
    scope: this
});

 

  上面是一般事件,还有快捷键注册的问题,事实上,Ext对快捷键这个功能的封装其实就是对keypress这个事件的改造。怎样定义快捷键映射呢,Ext.Element.addKeyMap(config)。所以,问题的重心又到了config这个东西了。我找到Ext.KeyMap这个类,研究一下:
  config的属性有:
  key:number/string/Array,例如:
    key: 13, // or Ext.EventObject.ENTER
    key: "a\r\n\t"
    key: [10,13],    //回车键被按了
    key: "abc"     //按了a或b或c
    key: "\t"
    由上可知,可以是设成单个按键,也可是多个按键,可是ascii码,也可以是那个字母。
  fn:Function
    相关联的处理函数,例如:
    fn: function(){ alert("Return was pressed"); }
  ctrl:Boolean
  shift:Boolean
  Alt:Boolean
  scope:Object

  

  总结上面,Element中的事件注册方法都差不多。

 

  二、关于组件上的事件注册
  组件的事件注册有它的特点了,尽管本质上还是on、un。如果用on、un,它的语法跟Element的语一样,没什么差别,关键是,组件允许在创建时的config中用listeners:{xxx:{},yyyy:{}}的形式的注册事件。不过,listeners里面的写法跟on的组合写法是一样的。这个我研究了。例如:
  listeners:{'select': {fn:this.sortImages, scope:this}}
  还是:事件名:options
  
  组件没有什么快捷键关联的功能,不过,能通过元素的快捷键注册功能来得到。这个没什么问题。Ext.Window

有一个Keys config的属性,用它可以定义快捷键。  

 

20.         extJs 2.0学习笔记(Ext.Panel篇一)

  老实不客气的说:没有Panel,就没有extjs的盛名。那些最常见的UI组件都是继承自它。暴爽的东西啊。我就在想,这么好的东西怎么会出现得这么晚呢?

  在这一篇中,将详细讲一讲Ext.Panel的方方面面。

  现在遇到了一些问题:

  一、显示的问题

  事实上,这个问题是所有组件的问题,凡是从Ext.Component继承的类都面临这个问题。

  例如,我写了一行这样的代码,但是没有任何结果:

  var panel=new Ext.Panel({width:300,height:300,title:'标题栏'});

  这是什么原因呢?

  if(this.applyTo){
    this.applyToMarkup(this.applyTo);
    delete this.applyTo;
  }else if(this.renderTo){
    this.render(this.renderTo);
    delete this.renderTo;
  }

  这几行代码是写在Ext.Component的构造函数中的。它标示如果applyTo、renderTo有值,就会在对象创建的时候直接呈现,如果这两值都没有,那就只能手工调用render函数了。

  然而这有一个问题,applyTo与renderTo倒底有什么区别,它们取值类型可以是哪些呢?看代码。

  applyTo的情况依赖于this.applyToMarkup来实现呈现。找到它的代码:

  applyToMarkup : function(el){
   this.allowDomMove = false;
   this.el = Ext.get(el);
   this.render(this.el.dom.parentNode);
  }

  而renderTo的情况是直接依赖于this.render(this.renderTo)的。这两者的差别很明显了,但是,这个问题到目前还不能说清楚,我发现,Ext.Panel最后生成的代码如下:

  <div id="panel2" class="x-panel" style="width: 300px;">
    <div id="ext-gen14" class="x-panel-header x-unselectable" style="-moz-user-select: none; cursor: move;">
      <span id="ext-gen18" class="x-panel-header-text">这是标题栏</span>
    </div>
    <div id="ext-gen15" class="x-panel-bwrap">
      <div id="ext-gen16" class="x-panel-body" style="width: 298px; height: 273px;">这是面板的内容!!!</div>
    </div>
  </div>

  由上代码可知,panel的代码总是外面一个容器:x-panel,然后里面几个,这儿是:x-panel-header、x-panel-bwrap。现在可以说一说renderTo与appplyTo的区别了。

  renderTo与applyTo的传入参数的数据类型与Ext.get的参数类型一样,可是dom、string、Element。它们最大的不同在于容器,这个容器不是指x-panel所对应的元素,而是指x-panel所对应元素的父元素。由源代码可知:

  当为applyTo时,它调用render(this.el.dom.parentNode);可见,x-panel的容器为applyTo对应元素的父元素。也即是applyTo事实上就是x-panel。而renderTo时,renderTo所对应元素是x-panel的容器。如何验证这个问题呢?请到FireBug中看一看就晓得了。

  上面说了一大通,我再总结一下:

  renderTo:对应x-panel所在div的父元素;

  applyTo:对应x-panel所在div本身。

  二、Ext.Component的几个极其重要的成员

  component.el:在panel中相当于x-panel所对应的div。它表示Component所对应的最外层html元素。

  component.id:在panel中相当于x-panel所对应的div的id,如果x-panel所在div没有id,那么就自己分配一个。

  component.container:它在panel中相当于x-panel所在div的父元素。即x-panel的容器。也即是:component的容器。

  如果没有分清楚这个问题,那么下面代码会产生问题:

  var p=new Ext.Panel({title:'my title',width:300,height:300,renderTo:'panel1'});

  console.info(Ext.getCmp('panel1'));

  结果如何呢?undefined!!

  为什么是这样呢,因为,getCmp用的id即是component.id。而这个id对应的是x-panel所在元素的id或者自由分配的。而renderTo对应的元素不是x-panel。而是x-panel的父亲。这个问题极容易搞错。

  这儿的两个问题其实都是Ext.Component那里的。下一篇正式研究一下Ext.Panel的API了。

 

21.         extJs 2.0学习笔记(Ext.Panel篇二)

  这一篇翻译自extJs 2.0官方文档。这篇是关于config的。

  我在网上查了很久,关于ExtJscore部分的中文文档还是有不少,但是关于panelwindow这些呢就好像不大齐全,而且,在js堂,它的文档翻译还在1.1。所以呢想翻译出来,以后大家也好查阅。

  本人js水平、英文水平都有限,还好,通过看源代码两相印证,终于还是搞出来了。欢迎各位提出宝贵的意见。事实上,只要搞定了panel,其也组件的config差不多。大同小异。嘿嘿。一通百通啊。

activeItem : String/Number
用于设置当前活动的子组件,取值为此子组件的序号或者是id。但是它只能应用于那种一次只能显示一个子组件的布局类,例如:Ext.layout.Accordion, Ext.layout.CardLayout和Ext.layout.FitLayout。

allowDomMove;Boolean
是否可以在组件呈现的过程中移动组件的dom节点。默认值为true。

animCollapse : Boolean
设置是否在面板收缩时起用动画,如果Ext.Fx有效(被包含进来)则默认为true,否则为false。

applyTo:Mixed
x-panel对应的div的id。


autoDestroy : Boolean
如果要把一个子组件从panel中移除且此值为true,则在移除的过程中自动会销毁此组件,返之,则不会,必须要手工销毁,默认值为true。


autoHeight : Boolean
如果为true,把this.el.dom.style.height='auto'。默认值为false。

autoScroll : Boolean
为true时,则把this.body.dom.style.overflow='auto'。默认值为false。

autoShow : Boolean
为true时,检查组件是否被设成隐藏,如果有,则移除这个效果。

autoWidth : Boolean
同autoHeight一样。。

baseCls : String
this.baseCls的class(默认值为'x-panel')

bbar : Object/Array
面板底部的工具栏。它可是一个Ext.Toolbar对象,也可以是一个toolbar的config对象,或者是一个要加入到工具栏的按钮的config的数组。注意:这个属性在render后就无效了,如果要在render后使用它,请使用 getBottomToolbar获得引用。

bodyBorder : Boolean
如果为true则为this.el对应的元素显示边框,默认值为true。这只在border==true时才有效。如果border==true且bodyBorder==false,那么将显示1px的inset边框。给予this.el inset的效果。

bodyStyle : String/Object/Function
要应用到this.el上的css class。它的格式需求与Ext.Element.applyStyle一样,默认值为null。

border : Boolean
也是设this.body的边框的,默认值为true,此时,默认情况下边框为2px。当然,它还会被bodyBorder影响。

buttonAlign : String
加入到面板中的按钮的对齐方式,合法值为:'right','left','cente',默认值为'right'。

buttons : Array
Ext.Button的config数组,用于加入按钮到面板的footer中。

cls : String
this.el的class。

collapseFirst : Boolean
当显示title bar时,是否总把收缩、展开按钮放在所有其他按钮的前面。默认值为true。

collapsed : Boolean
在呈现时,是收缩还是展开。为true则收缩,默认值为false。


collapsedCls : String
当面板处于收缩状态时,this.el所对应的class,默认值为'x-panel-collapsed'。

collapsible : Boolean
此面板是否可收缩或者说是否能显示收缩、伸展按钮。真为显示。默认值为false。

contentEl : String
一个已存在的dom的id。作用是用于在afterRender后把它this.body.dom.appendChild掉。默认值为''。

ctCls : String
设this.container的class。

defaultType : String
当在构造函数中用items填加新成员时,如果没有设xType,那么就会以这个默认类型为xType加入组件。默认值为'panel'。


defaults : Object
加入此组件的所有子组件的默认config。如果这些加入的子组件设了config的话就以新设的为准。例如:{bodyStyle:'padding:15px'}。


disabledClass : String
当组件被设成disabled时的css,默认值为:"x-item-disabled"。

draggable : Boolean
是否能被拖动。默认值为false。当然也可以是一个Ext.Panel.DD config。Ext.Panel.DD是一个internal但非公开的类(我没有找到它的源代码),它的作用是移动一个proxy元素(Element)以代替本应跟随鼠标移动的panel.el。但是它在拖动过程中、放下时不提供任何其他动作,也就是说,如果你不作处理的话,鼠标一松,panel仍然在老地方。它是Ext.dd.DragSource的子类,所以,必须通过实现Ext.dd.DragDrop的方法来产生动作。示例代码如下:

new Ext.Panel({
    title: 'Drag me',
    x: 100,
    y: 100,
    renderTo: Ext.getBody(),
    floating: true,
    frame: true,
    width: 400,
    height: 200,
    draggable: {
//      Config option of Ext.Panel.DD class.
//      It's a floating Panel, so do not show a placeholder proxy in the original position.
        insertProxy: false,

//      Called for each mousemove event while dragging the DD object.
        onDrag : function(e){
//          Record the x,y position of the drag proxy so that we can
//          position the Panel at end of drag.
            var pel = this.proxy.getEl();
            this.x = pel.getLeft(true);
            this.y = pel.getTop(true);

//          Keep the Shadow aligned if there is one.
            var s = this.panel.getEl().shadow;
            if (s) {
                s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
            }
        },

//      Called on the mouseup event.
        endDrag : function(e){
            this.panel.setPosition(this.x, this.y);
        }
    }
}).show();

 

elements : String
一个panel有五个部分:header、tbar、body、bbar、footer。elements就是保存当前panel包含了几个部分,例如,一个panel有header、body,那么:element=='body,header',默认值为:'body'。


floating : Boolean
为true的话,它会使panel.el.style.position=absolute。并且,默认情况下带有shimming和shadow。为false则不改变原有显示方式。
注意:把floating设为true会导致panel以offsets大量负偏移的方式隐藏。这个诸 位试一下就晓得了。所以呢,如果设了floating=true。那么,你render后最好还要setPostion(x,y)一下。当然如果你让面板浮动,也要把width设成一个固定值,不然,它会向右扩展到viewport的边缘。


footer : Boolean
为true则明确地创建footer,为false就不创建,默认情况下,如果对footer没有什么特殊的,那么当一个或多个按钮被加到footer上面时,footer会被自动创建。


frame : Boolean
为true的话呢就就在panel外面加上自定义的圆角边框,为false的话就是1px宽的长方形边框。


header : Boolean
为true时header被创建,反之不被创建,默认情况下,当header不处于特殊情况时,如果title被设置,它会被自动创建,否则不会被创建,如是果title被设置,但是header为false,那么header也不会被创建。


headerAsText : Boolean
为真是在header中显示title,为假时隐藏它。默认值为true.


height : Number
panel的高度,默认为auto。


hideBorders : Boolean
为true时,隐藏panel的所有子组件的边框,为false则尊从子组件原有边框设置。

hideCollapseTool : Boolean
当collapsible=true且hideCollapseTool=true时,则隐藏控制收缩、伸展的那个按钮,为false时就显示它,默认值为false。


hideMode : String
隐藏模式,有三种: "visibility" (css visibility), "offsets" (negative offset position) and "display" (css display) - defaults to "display"。

hideParent : Boolean
用于设置是否隐藏组件的容器,即component.container。

html : String/Object
一个html碎片,或者是满足DomHelper语法的object,它用于设置panel的body部分的内容。默认值为''。

iconCls : String
用于设置header上的图标的class。例如:.my-icon { background: url(../images/my-icon.gif) 0 6px no-repeat !important;}


id : String
一个为component统一分配的id值。默认值为panel.el.id。

items : Mixed
单个成员或一个子组件的数组。每个成员都可以是任何从Ext.Component继承的object。

它的成员可以是component的引用,这样就会马上render,也可以是component的config。这时就会lazy render。当然,在config中,要注意加上xtype。这个东西不用讲了吧。

关于xtype的所有取值情况,请见Ext.Component.xtype的config说明。里面有讲到。关于它的值,其实很多例子上都有,如果传一个成员,则像:items:{……},传多个的话呢,就像:[{……},{……}]。

keys : Object/Array
一个keyMap config object。用于设置快捷键的。默认值为null。


layout : String
设置panel.container的布局。如果没有设置,那么默认为Ext.layout.ContainerLayout,合法的值有:absolute, accordion, anchor, border, card, column, fit, form和table。如果要设置布局的细节,则要用到layoutConfig了。


layoutConfig : Object
用于设置布局细节的,当layout有合法设置时它才有效果。如果要知道关于这个config的设置细节,请见各布局类:
Ext.layout.Absolute
Ext.layout.Accordion
Ext.layout.AnchorLayout
Ext.layout.BorderLayout
Ext.layout.CardLayout
Ext.layout.ColumnLayout
Ext.layout.FitLayout
Ext.layout.FormLayou
Ext.layout.TableLayout

listeners : Object
一个config对象用于包含一个或多个事件handler,它被addListener使用来注册事件。

maskDisabled : Boolean
是否在panel.disabled的时候显示mask。为true显示。反之不显示。
默认情况下,panel哪怕在disabled时,它的子元素也显示得很正常,用户根本不知道这个panel被禁用了,这给用户带来困扰,但是,有了mask,用户就能得到提示,哦,这个panel是不可用的,被禁用了。这给用户带来了新的体验。


minButtonWidth : Number
panel上所有按钮的最小宽度,单位是px。

monitorResize : Boolean
为true时,它自动监控window的resize事件,并且让viewport因此而变化。这个东西的经典应用就是为layout服务,而不用我们手工去调整某些组件的大小来适应窗口大小的变化。


overCls : String
当鼠标放到panel.el上面时的class。最爽的是,当鼠标out时,它会被自动删除,从而产生hover效果。


pageX : Number
组件相对于页面的x坐标

pageY : Number
组件相对于页面的y坐标

plugins : Object/Array
一个对象或对象数组,它为component提供自定义的功能。每个对象都是一个插件的引用,当然,前提是这个插件定义了init方法,在component初始化时,这个init方法将被调用。没用着。不说了。难翻译啊。

renderTo : Mixed
Ext.get(panel.renderTo)就是panel.container。用语言说不清楚,这样直接了当。

shadow : Boolean/String
为true就给panel显示一个阴影,为false不显示。当然,也可设置成为shadow的类型,详情见Ext.Shadow、Ext.Shadow.mode。注意,这个选项只有在floating = true时才发生作用。

shadowOffset : Number
阴影偏移,默认值为4,只有在floating = true时才发生作用。

shim : Boolean
是否为组件创建shim,什么是shim呢?存在这样的情况,用div做的菜单,但是,好死不死有个applet或flash盖在上面的话,那菜单就会被盖在下面。这件事情曾经一度让b/s人员郁闷,ext提供一个通用的解决方案,在要避免这个问题的组件的同一位置创建一个与它大小一样的iframe,且使得这个组件的z-index大于iframe。由于iframe不会被其他东西遮住,所以,z-index在iframe之上的东西也不会被遮,iframe相当于个垫子,把我们要用的东西垫高了,而shim英语里面也是薄垫片的意思。高呼extjs万岁。当然,iframe的src必须为''。

stateEvents : Array
事件数组,当这此事件触发时,组件状态被保存。

stateId : String
用于管理组件状态的id,默认值为组件的id.

stateful : Boolean
一个标志,它表示组件在创建时是否从某个地方加载组件状态。哪些属性能作为状态保存呢?只有internal属性可以。
为了让组件状态能保存,组件状态管理器提供者必须实现Ext.state.Provider,也就是要实重写它的set、get方法以保存/重读键/值对,一个内键的提供者是: Ext.state.CookieProvider。

为当前页面设置状态提供者的方法如下:
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());

组件试图在stateEvents里面配置了事件时保存状态。你可以自己写点代码进行一处理,如在:beforestaterestore, staterestore, beforestatesave和statesave事件的处理代码中。

style : String
一个应用于panel.el上的样式,语法必须满足Ext.Element.applyStyles的接口。


tabTip : String
当panel是Ext.TabPanel中的一页时,为这个panel设tooltips的。不过,在render之前得先调用Ext.QuickTips.init()初始化一下。

tbar : Object/Array
panel顶部的工具栏,它可以是一个Ext.Toolbar,也可以是一个按钮数组或一个按钮的config。注意:在render之后,这个引用就没用了。如果要处理它请使用getTopToolbar。

title : String
显示在panel的header中的标题,当title被设置时,header就会被创建,除非header被设成false。如果你需要title,但不是在panel创建时,而是在之后的某个时刻,这时你需要为title设置一个非空值(如一个空格)或者是把header设为true。这样,panel在创建时才会创建header,不然,header将不会被创建。

titleCollapse : Boolean
当collapsible = true且titleCollapse=true时,用户点击panel标题栏的任意一处都会产生折叠/伸展效果,否则则只能通过单击那个按钮(上文有讲到)来产生这个效果了。


tools : Array
一个工具栏按钮数组,这个工具栏非同一般哦,不是tbar,也不是bbar,而是header上的标题栏,header上的标题栏是怎样的概念?你看到的panel的关闭按钮这个钮,超爽吧。每个工具栏元素以一个Element引用的方式向开发人员公开,通过
   tools.<tool-type>的方式引用。暴爽。
每个工具栏成员的config要包含下面属性:
id:string
  必需的,工具栏按钮的类型,可取值如下:

toggle (Created by default when collapsible is true)
close
minimize
maximize
restore
gear
pin
unpin
right
left
up
down
refresh
minus
plus
help
search
save
print

handler : Function
必需,当按钮被单击时被执行。它的参数说明如下:
  event:Ext.EventObject
  toolel:Ext.Element
  Panel : Ext.Panel宿主panel

scope : Object
qtip:String/Object
一个tips字符串或者是tips config,用于Ext.QuickTip.register.

hidden : Boolean
on : Object
自定义事件处理器的config,为addListener所用。


示例如下:
tools:[{
    id:'refresh',
    qtip: 'Refresh form Data',
    // hidden:true,
    handler: function(event, toolEl, panel){
        // refresh logic
    }
}]
  注意:除了toggle之外,其他工具栏成员都只是提供一个可视化的图标,没有任何功能,所以,如果你要加入它们,得自己写处理函数。


width : Number
component的宽度,单位用px,默认值为:auto。

x : Number
获得组件的x,相当于panel.el.style.left

xtype : String
这个东西不用说了,见Ext.Component的config里面的xtype。

y : Number
与x同理。

 

22.         extJs 2.0学习笔记(Ext.Panel篇三)

  上一篇中把panel的config部分的文档翻译了一下,事实上,好多东西都加上了我自己的看法,也不能说完全是翻译。真是个苦差使啊。这一次主要是来研究一下Ext.Panel的属性、函数。

属性:

body : Ext.Element
它是指向panel的body的Element引用。它被用于包含html内容。可以通过html config,或者是autoLoad config,又或者是通过panel的Updater来设置内容。此属性只读。
如果此属性被任何方法加载了html内容,那么这个panel就不能通过布局类来控制布局了。
如果这个panel已被布局类所管理,那么就不能对body进行任何改动,或加载什么内容。因为它正处于panel的布局管理类的控制之下。

buttons : Array
panel的button数组,它通过buttons config创建,只读。

dd : Ext.dd.DragSource.
如果此panel配置了draggable属性,这个属性将包含一个Ext.dd.DragSource的实例。开发人员通过必须提供对Ext.dd.DragSource的抽象方法的实现来达到支持drag/drop动作的目的。详情可见draggable。

disabled : Boolean
如果组件是disabled,那么它为true。只读。


footer : Ext.Element
指向panel footer的Element的引用。只读。它用于存放panel的buttons所定义的按钮。一般不要用它来放按钮。


header : Ext.Element
指向panel header的Element的引用。只读。这个元素用于存放title和tools。

hidden : Boolean
如果组件是隐藏的,那么它为true,只读。


initialConfig : Object
组件的config。只读。

items : MixedCollection
panel中的子组件的集合。

ownerCt : Ext.Container
组件的父组件,默认值为undefined,并且在加入到一个容器中时会被自动设置。只读。


rendered : Boolean
组件是否已经被呈现。。

 

公共方法:
Panel( Object config )

addButton( String/Object config, Function handler, Object scope ) : Ext.Button
config:如果是个字符串就被当作是按钮的text,如果是个object,那么它被当作config了。
handler:被按钮的Ext.Button.click调用。
scope:范围。
返回:被加入的按钮的实例。


collapse( Boolean animate ) : Ext.Panel
收缩面板


destroy() : void
看一下源码就知道,把组件挂接的事件、子元素什么的全部移除、delete。在什么Ext.ComponentMgr里面注册的东西也注销掉。


disable() : Ext.Component
没悬念。。使组件不可用。相当于大伙在dotnet中用得很爽的enabled。


doLayout( [Boolean shallow] ) : void
强制容器重新计算布局,就是刷新布局啦。它的作用就是当在render之后加入了一个组件时,这时就需要调用它来刷新一下了。又或者是改变了子组件的大小、位置,这时也要刷新一下。


expand( Boolean animate ) : Ext.Panel
伸展panel。


getBottomToolbar() : Ext.Toolbar
获得panel的bbar。

 

getFrameHeight() : Number
它的高度包括:header,tbar,bbar,footer,但是不包括body。单位是像素(px)。如果要获得body的高度,请使用getInnerHeight。


getFrameWidth() : Number
获得panel的边框宽度。当然是不包括body的宽度的,要获得body的宽度用getInnerWidth。

getId() : String
获得组件的id。


getInnerHeight() : Number
仅仅返回panel.body的高度。不包括其余四个部分。


getInnerWidth() : Number
仅仅返回panel.body的宽度。

getTopToolbar() : Ext.Toolbar
取得tbar的引用。

getUpdater() : Ext.Updater
通过Ext.Updater获得此panel的updater,有了它就能通过ajax更新panel.body了。

load( Object/String/Function config ) : Ext.Panel
通过一个XHR(XmlHttpRequest)调用马上加载内容。
示例代码如下:
panel.load({
    url: "your-url.php",
    params: {param1: "foo", param2: "bar"}, //或者是url编码的字符串
    callback: yourFunction,
    scope: yourObject,
    discardUrl: false,
    nocache: false,
    text: "Loading...",
    timeout: 30,
    scripts: false
});

这里面,唯一一个必须的属性就是url。


setIconClass( String cls ) : void
用于设置panel上的所有的icon的样式,如果哪个图标设置了样式,都会被它所替代。


toggleCollapse( Boolean animate ) : Ext.Panel
如果处理收缩状态就伸展,如果是伸展就收缩。总之与当前状态相反。


事件呢就不用了。


这个还是蛮容易的,个把小时搞定了。

 

23.         extJs 2.0学习笔记(Ext.Panel篇四)

  我刚才禁不住诱惑去看了一下Ext.Window的API文档,发现只是比Panel多了点什么最大化、最小化、关闭、置前、置后、动画引发目标设置、可调整大小这些功能。像什么标题栏、工具栏之类的东西在Ext.Panel早就封装好了。搞定了Ext.Panel终于可以闷声发大财罗。哈哈哈。

  这一文主要总结一下,panel的常见用法。

  一、使Panel的标题栏隐藏

  这是常有的事,常常,一个栏目并不需要标题。有什么办法呢,创建时config中加上:header:false。就ok了。

  二、如何使Panel能被拖动

  确保config中如下设置:

 var config1={title:'这是标题栏',
     width:300,
     height:300,
     header:false,
     floating:true,
     renderTo:Ext.getBody(),
     draggable:{
   insertProxy: false,
     
   onDrag : function(e){
   var pel = this.proxy.getEl();
   this.x = pel.getLeft(true);
   this.y = pel.getTop(true);
     
   var s = this.panel.getEl().shadow;
   if (s) {
     s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
   }
       },
     
  endDrag : function(e){
    this.panel.setPosition(this.x, this.y);
       }
     },

     html:'这是面板的内容!!!',
     layout:'fit',
     collapsible:true};

  好了,关键就是上面红色部分了,由前面的文档可知:Ext.Panel.DD这个类只是提供移动Proxy元素的效果,鼠标一松,panel仍在老地方,所以得写代码实现。关于Ext.Panel.DD,这个类是非公开的,我在源代码中也没有找到。不晓得放在哪。最后,我只得打开FireFox+FireBug才看到它里面的东西。

  为什么一定要floating:true呢?原因很简单,要能拖动,那么这个div就是absolute的,而一个panel.el默认不会是absolute。所以非得设成floating,以让它能浮动。

  三、如何有Panel能折叠、展开效果。

  这个很简单,panel在title tools中提供一个toggle的工具,就是用它干这事的,不过,默认情况下,panel的这个功能是被关闭的。panel还提供一个功能,单击title的任意一处都会产生toggle collapse效果。相关的config属性我提出来如下:

  animCollapse : Boolean

  collapseFirst : Boolean

  collapsed : Boolean

  collapsedCls : String

  collapsible : Boolean  //事实上,把它设成trueok了,其它几个可以不设置。

  titleCollapse : Boolean

  关于它们的使用说明请参见前面的API文档。不过,单看名字应当就晓得用了,取名很直观。

  四、怎样为panel设置title tools

  就是怎样为Panel的标题栏添加按钮,这个容器,在config中类似如下代码即可:

  tools:[{id:close,handler:function(event, toolEl, panel){panel.hide();}}],

  格式一般是:tools:[{……},{……},{……}]

  五、怎样显示、隐藏panel

  这个很容易,panel.show()/hide()就行了。但是,如果floating:true的话,那就麻烦了,show都显示不了,我在FireBug中一看,panel.el.dom.style.left="-10000px"。这当然是显示不出来的。所以,你show了还要setPosition一下。这才行了。

  六、为panel添加子组件

  简单,在config中加items属性,类似于如下代码:

  items:[{id:'win1',xtype:'window',title:'title',height:100,width:100}],

  格式一般是:items:[{……},{……},{……}]

  如果在定义时,没有设置xtype,则子组件创建是xtype取defaultType的值。像上面,由于window创建后是不显示的,所以,只得show出来。所以,我在定义时给它一个id。这样在Ext.onReady()中就可以:Ext.getCmp("win1").show()。嘿,就出来了。其他组件的加入类似。一个{}是一个组件。

  像上面那样的叫惰性呈现,还有一种写法是:items:[new XXX(config)]。这样的定义会在组件创建时直接呈现。

  七、为panel设置布局

  这个是关键了。就是要设layout、layoutConfig。这个要详细的讲一下。平常要用嘛。这儿用BorderLayout的文档上的代码为例:

     layout:'border',
     items: [{
       title: 'South Panel',
       region: 'south',
       height: 100,
       minSize: 75,
       maxSize: 250,
       margins: '0 5 5 5'
      },{
       title: 'West Panel',
       region:'west',
       margins: '5 0 0 5',
       cmargins: '5 5 0 5',
       width: 200,
       minSize: 100,
       maxSize: 300
      },{
       title: 'Main Content',
       region:'center',
       margins: '5 5 0 0'
      }],

  其实,布局类的api接口很简单,使用也不复杂。果然是把swing的那一套学过来了。

  Ext.layout.BorderLayout是一个比较特殊的布局类,它没有什么正儿八经的config,不像Panel它们。只需设一下layout,然后,在成员组件那儿用region标明是哪个区域就是了。Ext.layout.BorderLayout把一块地盘分成五个区:eastwestsouthnorthcenter

  我个人非常喜欢这个布局,因为以它作为外层布局的话,就能很方便的实现cnblogs一样的效果,我到现在还不知道那个左边列固定宽,右边列占满剩余屏幕的布局是怎么搞的,除非写javascript代码在window.onload中手工调,否则,全靠css,真的是想不出办法来,后来,我也找到一个办法:用table。table有一个特性,可以把它设成占满整个水平方向,第一列固定宽,第二列自动就是剩余的。不过,不晓得有没有纯css的解决方式,老实说,div+css实在太难控制了,table在水平方向上的布局优势还是很大的。比如,同一行,放两个内容,左边的左对齐,右边的右对齐,用div就麻烦了,又是浮动,挖空心思,用table就很显然,两个格:一个align=left,一个align=right。这话题扯远了。发牢骚罢了。

  八、怎样使得Panel能被改变大小

  事实上,如果真的panel能被改变大小,那么可以考虑使用window。只要把dragable设成false就成了。如果不喜欢那些按钮,应当也可以想到办法去掉的。panel本身没有这个功能。不过,ExtJs有一个类提供这种功能,即:Ext.Resizable。它的用法很简单,如下:

var resizer = new Ext.Resizable("element-id", {
    handles: 'all',
    minWidth: 200,
    minHeight: 100,
    maxWidth: 500,
    maxHeight: 400,
    pinned: true
});

  由上可知,它的功能是为一个指定的元素提供可改变大小的功能。这个功能不出奇吧。怎么把它用于panel。嘿,其实就一个关键,怎样取得x-panel所在元素的id。这个好办,事实上,这件事我曾在Ext.Panel篇一中就论述了。二种方法:panel.el或者panel.getEl()都一样。

  因此,代码应当这么写:

  var resizer=new Ext.Resizable(panel1.getEl(),{handlers:'all',pinned:true});

  resizer.on('resize'function(){panel1.updateBox(panel1.getSize());});

  在创建好panel之后,紧跟着加上这两行代码就行了。为什么要加上后面的那行呢?因为这个Ext.Resizable只会改变panel.getEl()的大小,但是里面的内容并不会因此而发生变化,结果是新增加的那一部分是灰色的。那是proxy的颜色吧。要把panel的子组件调整到实质大小必须要updateBox,可者是setWidth、setHeight。

  这个功能蛮简单,如果要对Ext.Resizable的详情进行了解,请参见其官方文档。

  九、怎样为Panel提供工具栏

  这个问题重要,panel提供两种工具栏:tbar、bbar。但是定义的方法都一样。首先,我要研究一下tbar的类型:Object/Array,api文档说:可以是toolbar对象、可以是toolbar的config、还可以是按钮的数组,当然也可以是前两者的数组。目标清楚了,就要小小地研究一下toolbar,toolbar这个主题比较大,事实上应当作为一个专题来搞的。不过,先搞清楚个大概用着先吧。

  这儿有一篇关于Ext.Toolbar的好文章,点此处查看!关于在Ext.Panel中使用工具栏,最直观、傻瓜式的用法,点此处查看。关于toolbar的更详细的研究、使用方法将在下一篇中给出。

 

  至此,关于Ext.Panel的常见问题都在这儿了,搞清楚了这些,基本上常规需求都可以实现了。

24.         extJs 2.0学习笔记(组件总论篇)

  组件正是extJs的亮点所在,像平常要用的Ext.Window、Ext.tabpanel都是我们最喜欢的好东西。要彻底弄清楚这中间的机制啊。

  我查到了一些参考资料:

  本来打算仔细分析一下Observable、Component、BoxComponent、Container这几个基类的源代码,但是不巧,正好看到了上面列出的教程中的“ExtJs实用开发教程”,这个系列教程写得太好了,以至于只要看一看教程就可以写出满足常见需求的代码来。而且,有了它,对于所有组件使用都有了一个大概映像。不至于不晓得如何下手,而非得去啃代码了。万岁啊。

  现在大概映像有了。继续看代码。

  Observable类是整个组件架构的最顶层的类。它的用途就是为组件提供统一的事件管理接口,有人问?怎么又是事件管理啊。Ext.Element不是已经封装了事件管理的机制了吗?也有人问:不是有了Ext.Element了吗,为什么还要Ext.Component呢?其实这两个问题是一件事。

  Component是组件,组件不是一个元素,而是若干个元素的有机集合。Element的确是有事件,但是,组件的事件不同于元素的事件,它是一个更高级别的概念。

  Observable类没什么了不起的。事件管理吗?其本质就相当于c#/java中的容器类,原理就是内置一个数组或者是{},然后提供一个接口来add、remove、removeAll、find。这些东西在c#/java中早就炒得滚瓜烂熟了。而所谓的addEvents方法本质就是为一个map加了一群的key。找到代码,果不其然,就是依赖于this.events,它初始值为{}。它怎样实现事件的注册、反注册、事件包装呢?说出来也蛮容易。内部依托于一些容器(像数组、json对象),然后把所有的要监听的元素、监听元素的事件、事件处理函数、scope、options都保存在里面,时机一至就fireEvent了。怎么包装事件呢?那段代码我也没有去看了,方法无法两种:

  一、对于原本就有事件,类为对应元素创建一个默认处理函数,在这个处理函数里面会FireEvent对应元素此事件的事件处理函数。

  二、原本没有事件,像Ext.Component的beforeDestory之类的事件,这种事件就要自己在相应的地方手工FireEvent了。

  Ext.Component类,这是个很重要的类。对此我只有两点要介绍:

  一、关注它的config。组件构造函数的形参Ext.Element/String/Object最为重要的是,形参可以是一个json对象,对于这个对象的各个成员的意义,这个是以后写代码的关键,例如:

  var wnd=new Ext.Window({titile:"这是伯平的标题栏",html:"这是窗口的内容!",width:400,height:300,layout:"fit"});

  上面代码是创建一个简单的div窗口,这是最普通的构造了,一般,还有一个items的成员,在它里面定义窗口的内容。items是一个数组,不过我与大伙一样都会产生一个疑问:只凭一个数组就能定义窗口吗?那些窗口成员的位置、大小怎么搞呢?关于样式,当然可以为每个数组成员填加一个成员cls,用它设class。但是,布局呢?难道全部是absolute不成?有门的。你看上面定义窗口的时候不是有一个layout吗?fit是一种布局方式,它的作用是,容器中只能一个可见成员,且这个成员会填充满整个容器。关于布局类这些东西,作者是参考自java的swing了。现在,就连dotnet的winforms也有类似的内容了。

  二、组件的统一管理,在Ext.Component的构造函数中有如下代码:

  Ext.ComponentMgr.register(this);

  extJs提供一个全局的类Ext.ComponentMrg。顾名思意:component manager。我跑过去看这个类的源代码,它内部用一个叫:Ext.util.MixedCollection的类来做容器,整个代码很少,加上注释才100来行。register就是往Ext.util.MixedCollection里面add而己。当然,有add就有remove。这儿也有两个东西要注意:

  1. 关于Ext.getCmp(id)
    getCmp : function(id){ return Ext.ComponentMgr.get(id); }这是Ext类中的代码,这个get现在能理解它的用途了吧,也就是说,只要一个组件被创建出来,它就会被加到这个容器中来,然后随时可以用它的id取出来继续用。所以,你甚至不用保存创建的组件的实例。可以直接用getCmp取出来。
  2. 关于Ext.reg

它用于注册一种组件类型,这个东西我在以前也没有发现它的作用,现在看了Ext.Component才明白。且看它的代码:
registerType : function(xtype, cls){
            types[xtype] = cls;
            cls.xtype = xtype;
}
xtype就是新注册的组件类型,它是字符串,而cls不是class哦,它是这个类对应的function,文档上说是这个类对应的构造函数(就是constructor啦)。真相大白了。

  关于Component,我要说的就这么多:一个config,一个组件管理器。

 

  Ext.BoxComponent

  Ext.BoxComponent继承自Ext.Component,它的主要作用是封装元素的设置位置、宽度的功能。有人说,位置、宽度的设置在Ext.Element不是早就封装了一大把吗?这是个编程技巧,在组件这种高层面上,元素是低层面的,应当尽量把这些内部实现的东西隐藏。不然,不管三七二十一什么事都去访问Ext.Component.getEl()方法,然后为所欲为,那就打破了封装性了。这相当于设计模式中的“外观模式”。功能早就有,只是提供一个高层的接口来访问罢了。

 

  Ext.Container

  Ext.Container继承自Ext.BoxComponent。对于它,我先介绍一篇分析得不错的文章!点此处进入

  一句话,这个类是封装布局细节的类,用专业的术语来讲,它是extJs中一堆布局类的外观类,通过它,我们基本上不要去管那些布局细节的事情了,只要在创建组件实例时的config中,加入一个layout:XXXX就成了。如果要对layout初妈化配置,传说中,Ext.Container的config json提供一个叫:layoutConfig的成员。它的详细文档就得看API了。当然,这儿我还是要点一下一些最常用的API。

  config object:width/height/activeItem/defaultType/default/items/layout/layoutConfig/listeners/moniterResize

  常用属性:items/ownerCt

  常用方法:add/insert/remove/findXXXX

  你看,这个使用简单啊。那么是不是所有其他组件都是继承自Ext.Container呢。当然不是,只有像Ext.Panel、Ext.TabPanel、Ext.Window这种复合型的组件才是从Ext.Container继承的。

  到此为止,一定有人对于使用常用组件有些迫不急待了,点此处让大伙来瞧瞧

 

  好了,大概就是这样了,下面提供一个ExtJs 2.0的组件结构图:

  

25.         extJs 2.0学习笔记(Ext.Element API总结)

  Ext.Element API比较多,大伙用的时候也难以在短时间把住它的脉络,主要功能。这个给个总结,而不是一个API说明。说到API说明,网上早有大侠做得蛮不错的了。

位置设置:

getX()   取得相对于页面的x坐标
getY()   取得相对于页面的y坐标
getXY() 取得相对于页面的x,y坐标,用法:ele.getXY()[0]/[1]
getOffsetsTo(el)  取得相对元素el的坐标,返回值[x,y]
setX(x,animate)   设相对于页面的x坐标,animate为true则开启默认动画效果
setY(y,animate)  同上
setLeft(left)   设style.left
setTop(top)    设style.top
setRight(right)  设style.right
setBottom(bottom) 设style.bottom
setXY(pos,animate) 设相对于页面的x,y,相当于setX,setY。用法:setXY([x,y],true)
setLocation(x,y,animate) 相当于setXY。
getRegion()        return {top=t,left=l,width=w,height=r}
getHeight(true|false)    true不包括边框、内边距,false包括
getBorderWidth(anthor)
getPadding(anthor)
getComputedHeight()  包括边框、内边距
getComputedWidth()
getSize()   相当于getWidth、getHeight   {width:w,height:h}
getStyleSize()   取style.width、style.height,且不包括边框{width:w,height:h}
getViewSize()    取视口大小{width:w,height:h}
getValue()     如果有value属必就取它
setWidth(width,animate)     设style.width
setHeight(height,animate)    设style.height
setSize(width,height,animate)
setBounds(x,y,width,height,animate)   相当于setSize、setXY
setRegion(region,animate)      相当于setBounds
getScroll()            取得当前视口在文档中水平、垂直方向上的偏移,返回{left:l,top:t}

setOpacity(opacity,animate)
getLeft(local)    false相当于getX,true相当于style.left
getRight(local)   false相当于getX+getWidth,true相当于getLeft(true)+getWidth()
getTop(local)   false相当于getY,true相当于style.top
getBottom(local)
position(pos,zIndex,x,y)  string:pos,取static,relative,absolute,fixed
                          zIndex设z序,x,y用来调用setXY的
clearPositionsing(value)  作用非得看代码
    clearPositioning : function(value){
        value = value ||'';
        this.setStyle({
            "left": value,
            "right": value,
            "top": value,
            "bottom": value,
            "z-index": "",
            "position" : "static"
        });
        return this;
    }

getPositioning()  取值格式如上{……}
setPositioning(pc)  用法同applyStyles
setLeftTop(left,top)  就是设style.left,style.top


moveTo(x,y,animate)    相当于setXY
move(direction,distance,animate)   很有用,direction移动方向,取t,l,r,b。
                    distance是移动的距离,animate是否启用动画效果

getAnchorXY(anchor,local,s)   取得对齐到某处的页面x,y坐标
getCenterXY()          取得对齐到视口中央的页面x,y坐标
getAlignToXY(el,p,o)       取得对齐到某元素某处的页面x,y坐标

alignTo(element,position,offsets,animate)  对齐到元素的某处,允许偏移、动画
anchorTo(el,alignment,offsets,animate,monitorScroll,callback)  对齐到元素


center(centerIn)   对齐到视口中央
getBox(contentBox,local)  contentBox=false包括边框、内边距 local=false获取页面坐标
setBox(box,adjust,animate)   box包括边框、内边距
getFrameWidth(sides,onlycontentBox)

repaint()    强制刷新元素

 


关于样式、属性设置

addClass(String/Array className )
removeClass(String/Array className)
replaceClass(String oldClassName, String newClassName )
radioClass(String/Array className )
toggleClass(String className )
setStyle(name,value)
getStyle(name)
hasClass(className)

addClassOnClick(classname)
addClassOnFocus(classname)
addClassOnOver(classname)
hover(classsname,bool preventFlicker)

页点操作:
append(ele)
appendTo(ele)
replace(ele)
replaceWith(ele)
insertBefore(ele)
insertAfter(ele)
insertFirst(ele)
insertHtml(where,html,returnEl)
insertSibling(el,where,returnDom)
remove()
createChild(config,HTMLElement insertbefore,returndom)


first(selector,returndom)
last(selector,returndom)
next(selector,returndom)
prev(selector,returndom)
parent(selector,returndom)
child(selector,returndom)
up(selector,maxdepth)
down(selector,maxdepth)

query(selector)

contains(HTMLElement/string el)

show(animate)
hide(animate)
toggle(animate)
setVisible(boolean visible,animate)


update(html, loadScripts, callback)

特殊的
hover( Function overFn, Function outFn, [Object scope] ) : Ext.Element
mask(msg, msgCls)
unmask()
load( String/Function url, [String/Object params], [Function callback], [Boolean discardUrl] )
focus()
blur()


事件注册:
on(eventname,fun)
un(eventname,fun)
addListenser/removeListenser/removeAllListeners

 

  在上面,我把Ext.Element的所有API进行分类,平常要完成什么功能,一查即知,而且,我这儿给出那些只有细微差别的API的不同之处。让人一看即知,当然,如果对最基础的API都有疑问,这儿有一篇好文章,可以补此不足,点此处访问它

26.         extJs 2.0学习笔记(Element.js篇)

  Element.js这个文件包含了整个extjs框架中最为核心的部分,它对DOM元素进行超强的封装。源文件就有3054行,尽管这中间有好多是注释,但是,在个把月前,我不敢想像我会要拿着几千行的js文件来研究。呵呵。

  就我目前而得知的情报,Ext.Element类至少包含如下功能:

  一、为许多DOM动作创建Animation(动画),例如setWidth,它提供一个可选项来获得动画效果

  二、提供一个伟大的load方法,可以直接ele.load({url:xxxxx}的方式来异步加载数据。超爽、超酷啊。

  其他功能我目前不知,这是看了别人的教程有了这样一个大概的映像。如果要对它获得一个大概映像,点此处访问相关资源!通过这个资源,几乎就可以动手写程序了。

  废话就不多讲了,还是来研究研究源代码。

  一、Ext.Element的缓存机制

Ext.Element = function(element, forceNew){
    var dom = typeof element == "string" ?
            document.getElementById(element) : element;
    if(!dom){ // invalid id/element
        return null;
    }
    var id = dom.id;
    if(forceNew !== true && id && Ext.Element.cache[id]){ // element object already exists
        return Ext.Element.cache[id];
    }

    this.dom = dom;

    this.id = id || Ext.id(dom);
};

  Ext.Element提供了缓存机制,作用:提高性能;原理:让构造过Ext.Element存放在json对象中,如果要再次获得这个对象就不用再次构造,只需从缓存中取出即可。原理很简单。

  上面函数是Ext.Element的构造函数,其中forceNew表示是否强制创建一个新对象,不管缓存中是否已存在。这个构造函数还强制为元素生成id。

  缓存定义在哪?在代码的最后面有一行代码:El.cache = {};可见,缓存是全局的。这是必然。再看看大伙平常用得最多的函数。Ext.Element.get。这个函数都有四十几行的代码,因为整个函数考虑到可能传入参数的类型有好几种的情况,情况多起来,所以有点多。

  二、Ext.fly倒底有什么用?

  Ext.Element有一静态成员fly。它也是获得一个Ext.Flyweight对象的实例。当然,它也是对元素的包装,请见代码:

  var flyFn = function(){};
  flyFn.prototype = El.prototype;
  var _cls = new flyFn();

  // dom is optional
  El.Flyweight = function(dom){
   this.dom = dom;
  };

  El.Flyweight.prototype = _cls;
  El.Flyweight.prototype.isFlyweight = true;

  看清代码了吧?可以说,Flyweight是继承自Element的。不过,正如其名,它是轻量级的。为什么这么说呢?见fly的代码:

El.fly = function(el, named){
    named = named || '_global';
    el = Ext.getDom(el);
    if(!el){
        return null;
    }
    if(!El._flyweights[named]){
        El._flyweights[named] = new El.Flyweight();
    }
    El._flyweights[named].dom = el;
    return El._flyweights[named];
};

  这个代码的关键是,它总是会取得同一个Flyweight对象,用fly取的时候,只要没有传named,所有取的对象都同一个,换的只是dom罢了。

  有人问这有什么好处?好处大了,如果有1000个元素,要调用它们的hide隐藏,如果用Ext.get的话,就会创建1000个Element对象,如果用fly,那只会创建一个对象。这对性能带来巨的提升。所以,在有一些批量式的操作时,用fly要好一些。

  三、对Element.findParentElement.findParentNode的质疑!!

  原来如此,本来以为它是寻找的对象不包括自己,我总以为没有得到结果,原来,这两个函数是从自己找起的,如果一个元素是div,如果它用findParent找div祖先的话,那找的总是自己。

  这个问题是解决了,不过,我对findParent的代码有疑问,它里面用Ext.getDom(maxDepth)来获得最极限层次的那个节点,事实上,我把getDom传数字的话只会返回数字本身,根本不会返回元素。这个问题我一直想不通。不过,这并不会影响表面上的功能。只是设maxDepth相当于白设了。

  四、Element的动画支持

  Element要使用动画有三大类方法。

  1. 直接调用Ext.Elementanimate函数,它建立于Ext.lib.Anim类的基础之上,而Ext.lib.AnimAdapter里面的类。
  2. 在调用Ext.Element的一些设置大小、位置、范围、透时度时,那些函数往往有一个参数,anim,设一设它,也能获得动画效果。
  3. 调用从Ext.Fx类中继承过来的方法,如果页面包含了Ext.Fx,那么就可以用所有在Ext.Fx中定义过的函数了。

  一般多用后面两种方法。出人意料的是,Ext.Element继承了Ext.Fx。代码上反应在哪里呢,在Ext.Fx的最后面,有这样一行代码:Ext.apply(Ext.Element.prototype, Ext.Fx);这样一来,在Element中就可放心地使用特效了。关于对Element使用动画,这个现在不是研究重点,这个Ext.Fx要研究的内容。在此一笔带过,实在急于这个问题的,可以点此处阅读相关参考资料

  五、设置位置、大小、绝对位置、相对位置等等于关定位、范围的函数

  这是个大问题,事实上,前一周我重点就在搞这个问题,例如:

  1. 如何取得相对于定位容器的坐标
  2. 如可取得相对于页面文档的坐标
  3. 如何取得元素的大小,包括边框、内边距、实际内容
  4. 如何取得相对于元素的坐标(偏移量)
  5. 如何取得视口大小
  6. 如何取得当前文档水平滚动、垂直滚动的距离
  7. 如何取得当前元素相对于视口左上角的坐标

  问题多吧。这些问题在平常计算时多要用到这些量,然而这些量与浏览器类型严重相关。不同浏览器取法不一样。麻烦、头痛啦。还好,这个问题Ext.Element都对此进行了封装。不用我们烦心了。当然,上面还仅仅是取得,还有设置这些量的问题,这就至少十五、六个函数了。Ext.Element又是英文文档,给我们的理解造成了困难呐。下面来把这些东西找出来。到了这里,我再次想起asp.net ajax,这个东西现在与extJs比起来屁都不是啊。在asp.net ajax中,就封装一丁点的东西,它里面的DomElement相当于Element了,只是功能完全不能比,就只有create、addClass、removeClass、toggleClass,关于我上面提出的七个问题,一个都没有解决。这个框架有跟没有一样。如果有兄弟正在用asp.net ajax,最好还是换一换,换谁都不要用asp.net ajax啊。

  关于这个问题,我一并交到下一文中,下一篇中主要是把Ext.Element中的API归类,比如要创建元素节点并插入某个地方,有哪些方法?

27.         extJs 2.0学习笔记(DomHelper.js篇)

  这个文件定义的是Ext.DomHelper类。作用是提供一些Dom的操作。这个类不是静态类。

  这个类第一个函数createHtml是一个私有函数,用于从指定转入创建对应的html文本。这个类有多种情况,根据源代码分析:

  var createHtml = function(o){}

  o的取值可能有:string(现成的html字符串)、Array(JSON对象数组)、object(JSON对象)

  可见,createHtml的主要作用是把一个JSON序列化为html字符串。那么,createHtml能识别的JSON对象有什么格式要求呢?当然有,描述如下:

  {tag:string,          //元素的标记名,如果没有,默认为div

  children|cn:string|Array|json,  //子结点对应的json数组或字节点的html或单个json

  html:string,          //对应的html,如果有cnchildren属性就忽略

  style:function|string|json,    //元素的样式,可以是函数,字符串,json对象

  cls:string,           //元素的class属性的值

  htmlFor:string         //元素的For属性,

  x:y              //x表示其他名字,y表示非函数、非空内容

  }

  我发现,style尽管允许是放一个function,但是,如果真是function的话,那么对应的style并不会生成,因为如下代码:

  if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;

  如果值是函数,就continue了。这应当是extJs程序员不小心的bug。

 

  下面有一个私有函数:createDom,定义为var createDom = function(o, parentNode){……},用于根据一个o创建dom结构树,并把它放到parentNode这个节点下。o的情况跟上面的createHtml一样。这儿我学到的是:documentFragment,这个函数用于创建一个文档碎片,为什么用它呢?点此处见详情!document结构下,每appendChild一次就引发一次树的刷新,这将影响性能,而使用Fragment,相当于先将所节点创建好好一缓存,然后一次性appendChild到document(注意,这儿的document泛指文档树)下。

 

  var insertIntoTable = function(tag, where, el, html){……}

  它的作用是往表里面插入单元格或行、或tbody或在table之前或之后插入指定html内容的文本。即:

  tag:null/td/tr/tbody

  where:beforebegin/afterend/afterbegin

  el:节点的引用

  html:要插入的内容

 

  后面就是insertHtml了,这个函数是关键,可惜里面好多函数不熟,整个DomHelper中的那些函数,好多都是建立在它的基础之上的。像什么:insertBefore、insertEnd、insertFirst、append。overwrite是重写已有节点的innerHTML用的。至此功能倒是齐全了。

  其实,总结Dom操作,无非就是插、删、修改。这儿封装的都是插入,因为修改简单,改它的innerHTMl就是了,况且这儿还有一个overwrite。删也简单,有Ext.destroy。Ext.Element.remove。

  

  今天精神有点不好,我看了一大把的js文件。每个文件的代码量都不少。这要看到什么时候啊。不能这么干了。不过,还是打算花大力气研究一下Ext.Element。

28.         extJs 2.0学习笔记(ext.js篇)

 要是以前,我铁定整天到处找教程看,光说不练,现在觉悟了,看教程看得最多,不一定能看完,看完了不一定能比作者更明白,看明白了不一定能用得好。所以看教程其实好处不大,只能作为小小的参考。很多东西看别人的始终是没有用。只有将实验进行到底才是王道……

  这儿主要是代码分析。

  研究工具:Dreamweave cs3(装那个extJs 2.0插件老装不上)Aptana(一个好处,好看代码,有括号匹配,json语法好是好,就是括号多了,搞清在哪儿结束)

  发现,extJs的代码最喜欢用json语法定义,类基本上都是用json语法定义的。而不是在外面一大路的xx.prototype.yyyy=function(){……}。不过这种语法蛮清晰。我喜欢。

  extJs时面只有一个类:Ext,它是一个静态类。提供了经常要用到的函数。

Ext.apply = function(o, c, defaults){
    if(defaults){
        // no "this" reference for friendly out of scope calls
        Ext.apply(o, defaults);
    }
    if(o && c && typeof c == 'object'){
        for(var p in c){
            o[p] = c[p];
        }
    }
    return o;
};

  这是apply函数,作用其实相当于克隆,它把对象c中的成员全部复制到o中去。如果有defaults,也把它的内容复制到o中。这儿其实揭示javascript的一种语法:

  javascript中的对象的成员有两种引用方法:

  一、o.propertyName

  二、o[propertyName]

  这段代码关键就在o[p]=c[p]。这个要理解。尽管如此,但是不能像下面一样做:

  var newelem=new Object();
  Ext.apply(newelem,Ext.getDom("a1"));
  Ext.getDom("form1").appendChild(newelem);

 

  下面一大段的代码,由于dw不好看代码,半天才晓得那儿是个(function(){……Ext.apply(Ext,{……}})(),这是我把概述出来。这样写呢,实在有点叫人别扭,作者的意图是想把这相关的一段全部放到括号中,以免造成理解上的混乱。能理解。不过,这种写法不大招人喜欢。

 

        applyIf : function(o, c){
            if(o && c){
                for(var p in c){
                    if(typeof o[p] == "undefined"){ o[p] = c[p]; }
                }
            }
            return o;
        }

  这是applyIf的代码,事实上,在文档上面,它的描述有问题,应当是是当o,c两个对象都存在时,则把o中不存在,c中存在的属性复制到o中,且属性名不变。而不是所谓“如果o不存在时就把属性复制到o中”,哪有这种说法的。另外,判断一个对象是不是存在,最严谨的还是用typeof的方法。

 

addBehaviors : function(o){
            if(!Ext.isReady){
                Ext.onReady(function(){
                    Ext.addBehaviors(o);
                });
                return;
            }
            var cache = {}; 
            for(var b in o){
                var parts = b.split(
'@');
                if(parts[1]){ // for Object prototype breakers
                    var s = parts[0];
                    if(!cache[s]){
                        cache[s] = Ext.select(s);
                    }
                    cache[s].on(parts[1], o[b]);
                }
            }
            cache = null;
        }

  这个地方巧妙在于依赖于Ext.isReady。这个属性我估计应当是在onload第一行将它设成true的,它的作用就是用于标志当前是不是已经文档模型加载完了。前面几行的意思:如果dom模型还没有加载完,没有准备好,就将这些事件注册代码交给onload去做。即Ext.onReady。

  如果DOM模型已加载完,那么就马上注册事件,区别:前者是延迟注册、后者是马上注册。为什么要延迟,因为DOM都没有创建完,有些元素在DOM树中还不存在,当然就没法设置它了。其余的地方则不足道,后面的关键就是Ext.select了。

 

        id : function(el, prefix){
            prefix = prefix || "ext-gen";
            el = Ext.getDom(el);
            var id = prefix + (++idSeed);
            return el ? (el.id ? el.id : (el.id = id)) : id;
        }

  这儿有一个技巧:prefix = prefix || "ext-gen",这是最简捷的代码啊。本来要一个if语句的。

 

  extend、namespace两个函数硬是没有看懂,等水平高了再来研究。

 

  urlEncode的源代码原理简单,但是,要是我的话还是没法写得这么清楚,主要是情况比较多。这儿主要是学到了数组的push,原来以为push只能传一个参数,没想到能一次传多个。发现,很多时候,在构造一个复杂的字符串时都是用到数组的。至于urlEncode的作用,就是把一个JSON对象编码成一个查询字符串。

        each : function(array, fn, scope){
            if(!Ext.isArray(array)){
                array = [array];
            }
            for(var i = 0, len = array.length; i < len; i++){
                if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
            }
        }

  这个函数的功能并不是像它的名字一样简单啊,这儿又学到了:

  一、原来构造单元素数组可以直接这样写:a=[a]。

  二、scope在这儿是默认伪调用者,同时,还把当前数组元素值、序号、数组引用都传过去了。这个可能在fn中用得着。要注意。

  另外就是x===false这个语句要注意。要知道undefined==false。

        callback : function(cb, scope, args, delay){
            if(typeof cb == "function"){
                if(delay){
                    cb.defer(delay, scope, args || []);
                }else{
                    cb.apply(scope, args || []);
                }
            }
        }

  吃了一惊,Function什么时候有个成员叫defer了?后来才知,defer是extJs扩展出来的。delay是时延。老实说scope这个东西不能言传只可意会,不看代码是不清楚的。事实上javascript中的确是存在defer属性的。用于修饰script元素的,确实是用于延迟script里面内容的加载。详情见此处

        destroy : function(){
            for(var i = 0, a = arguments, len = a.length; i < len; i++) {
       var as = a[i];
      if(as){
        if(typeof as.destroy == 'function'){
          as.destroy();
      }
      else if(as.dom){
          as.removeAllListeners();
          as.remove();
      }
                }
            }
        }

  这个函数用来销毁对象,由代码可知一点,extJs鼓励大家在创建自己的类有必要的话就写destroy。如大量没用的dom元素。在这里,destory相当于析构造函数一样。至于removeAllListenners,remove这两个函数,它们是Ext.Element类的成员。

  removeNode : isIE ? function(){
            var d;
            return function(n){
                if(n && n.tagName != 'BODY'){
                    d = d || document.createElement('div');
                    d.appendChild(n);
                    d.innerHTML = '';
                }
            }
        }() : function(n){
            if(n && n.parentNode && n.tagName != 'BODY'){
                n.parentNode.removeChild(n);
            }
        }

  这个代码作用显然,就是删除一个结点。但是这个代码的写法实在有点让人难以接受啊。最郁闷是如果ie,那么,那个参数n是怎么传进去的呢,因为外面罩住的那个函数没有参数,本来没有参数也好办,关键是外面的那个函数根本没有传参数给return里面的函数,这居然也能传进去,见识到了。

  经过一番实验与琢磨,发现,其实并不是外面的函数能传参给里面的那个函数,实在是因为那个()用得好,如有:

  var do1=function(){return function(n){}}();

  关键是要外面的函数{}之后要马上“自调用”一下,这样就会返回一个结果,这个结果是个函数表达式,它就能传参了。所以如果外面的函数没有()的话,那么实际调用将必须写成:do1()(3)的形式,连写两个括号。。这个问题我想了好久,终于想清楚了。

    createCallback : function(/*args...*/){
        // make args available, in function below
        var args = arguments;
        var method = this;
        return function() {
            return method.apply(window, args);
        };
    }

  顾名思意,回调。这个函数是对Function对象的原型扩展。所以,所有函数都有这个成员。例如:

  function A(){}

  B=A.createCallback();

  B();

  最后B()执行调用的是A。有人说,既然调用B就相当于调用A,还不如直接用

  function B(){A.apply(window,this.argments);}

  的确,这样确实可以达到差不多的目的,但是,写代码要注意封装。尽管这只有一行代码,但是,相对于客户程序员来说,createCallback比apply亲切多了,而且,它还节省了不少字符,这就节省带宽。

  什么时回调?让别人来调,那为什么不定义在那个调用者里面?因为,只有定义在别人的里面才可以获得别人的信息。

  当然,在这儿我还是学到了一点,以前没意识到,怎样把外层的this传给内层的function。只需method=this。

  有一些Ext下的函数并没有定义在ext.js中。如:Ext.onReady、Ext.reg、Ext.select、Ext.query。

 

posted @ 2013-07-02 15:58  若 ♂ 只如初见  阅读(2405)  评论(0编辑  收藏  举报