前言
组件化编程:是将各种小部件组装成一个对象,并提供相应的事件和函数,供别人方便的调用.
本文基于EXTJS 3.0.0,在其他版本中(特别是EXTJS2.0)会有所不同请注意!!
正文
EXTJS的效率一直都被关注着,效率问题也是所有web开发者面临的最大的难题.面对着EXTJS3.0超过600KB的体积,被无数人指责,唾弃.我觉得这些人根本就没有从问题的本质出发去看待这个工具.
我一直把EXTJS作为一个工具来看待,哪个工具好,我就用哪个.如果根据系统情况Jquery或者Prototype能有更好的表现,我是很乐意 去使用这些AJAX框架的.一个工具的推出,必然有它的优点和缺点,只是要看你怎么取舍.EXTJS的效率慢,我承认,确实不如其他的一些AJAX框架. 但是这个慢,是建立在其丰富的UI组件,统一的界面风格上的(当然,看多了也腻).就好比Hibernate,你能说Hibernate不好吗?但是,Hibernate的效率肯定不如JDBC直连.
或许很多人会说,我就是不用EXTJS,我看见它就不喜欢.说这种话的人实在太不负责任,自己没有时间去学,或者直接就是害怕去学它,也不愿意别人 去学..我在此强烈谴责这些说话不负责任的人.这类人应该从思想素质上去提高.这是人性的问题,已经超出技术范围了,这种人发展到最后也就是孔子的那句话(改编):”老而不新(死),视为贼也”.
关于EXTJS的效率提升.现在很多人在学习EXTJS,每个人的写法各不相同,我只是将我自己的一些经验说一下.
首先是面向对象的思想建立,EXTJS虽然是基于Javascript的,但是在对象上,写法确实跟Javascript不太一样.EXTJS让Javascript的面向对象更接近强类型后台语言的写法,现在写EXTJS的有很多人没有按照正确的EXTJS式的面向对象的思想去写,例如一个简单的对象:
- Ext.ns(“Ext.ux.Panel”);
-
- Ext.ux.Panel=function(){
-
- return new Ext.Panel();
-
- }
Ext.ns(“Ext.ux.Panel”);
Ext.ux.Panel=function(){
return new Ext.Panel();
}
这样写是没有错误的,EXTJS底层很多也是这样写的例如基类Observable
- EXTUTIL.Observable.prototype = function(){
- var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){
- return s.toLowerCase();
- };
-
- return {
- //省略若干代码
- }
EXTUTIL.Observable.prototype = function(){
var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){
return s.toLowerCase();
};
return {
//省略若干代码
}
这就是Javascript最常用的写法,但是在
开发过程中我们能这样去写EXTJS对象吗?
在大多数情况下这显然是不可行的,当整个项目采用Extjs编写之后,这样的代码会让接手的人有一种无从下手的感觉(这个人可能就是两三个月后的你自己).
我们应该怎么样去写一个对象让代码看起来更简洁清晰呢?还是查看EXTJS的源码,UI组件Window最能说明问题:
- Ext.Window = Ext.extend(Ext.Panel, {
- //省略若干属性
- baseCls : 'x-window',
- //.......省略若干属性
- initComponent : function(){
- Ext.Window.superclass.initComponent.call(this);
- //省略若干函数体
- }
- //.......省略若干函数
Ext.Window = Ext.extend(Ext.Panel, {
//省略若干属性
baseCls : 'x-window',
//.......省略若干属性
initComponent : function(){
Ext.Window.superclass.initComponent.call(this);
//省略若干函数体
}
//.......省略若干函数
Window是继承自Panel的,并为Panel添加了拖拽等功能,到这里,一切就真相大白了,Panel继承自BoxComponent,理论上来讲,我们自己的组件完全可以基于Panel来做,我们可以暂且将Panel作为我们所有组件的容器,我们所写的各种组件都可以被包在这个Panel里面去.
我们首先构建出自定义组件(这里以登陆为例)的轮廓:
- Ext.namespace("Lesson1.FirstPanel");
- /**
- * @author andy_ghg
- * @version 2009年9月24日23:22:09
- * @description 第一个派生类
- * @class Lesson1.FirstPanel
- * @extends Ext.Panel
- */
- Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
- frame:true,
- //初始化函数
- initComponent:function(){
- //继承父类的initComponent函数
- Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
- }
- });
-
- //*测试
- Ext.onReady(function(){
- var panel=new Lesson1.FirstPanel({
- title:"标题",
- height:300,
- width:400,
- renderTo:Ext.getBody()
- });
- });
- //*/
Ext.namespace("Lesson1.FirstPanel");
/**
* @author andy_ghg
* @version 2009年9月24日23:22:09
* @description 第一个派生类
* @class Lesson1.FirstPanel
* @extends Ext.Panel
*/
Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
frame:true,
//初始化函数
initComponent:function(){
//继承父类的initComponent函数
Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
}
});
//*测试
Ext.onReady(function(){
var panel=new Lesson1.FirstPanel({
title:"标题",
height:300,
width:400,
renderTo:Ext.getBody()
});
});
//*/
运行之后就是一个很普通的Panel,不要着急,我们还要往里面添加东西,登陆需要一个表单,我们继续写下去为这个组件添加一个表单:
- Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
- frame:true,
- //初始化函数
- initComponent:function(){
- //继承父类的initComponent函数
- Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
- //为该组件添加了一个FormPanel
- this.formPanel=new Ext.FormPanel({
- defaults:{anchor:"95%",allowBlank:false},
- labelWidth:55,
- defaultType:"textfield",
- items:[{
- fieldLabel:"用户名",
- name:"username"
- },{
- fieldLabel:"密码",
- inputType:"password",
- name:"password"
- }]
- });
- //将表单列入到panel的items里去
- this.add(this.formPanel);
- }
- });
Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
frame:true,
//初始化函数
initComponent:function(){
//继承父类的initComponent函数
Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
//为该组件添加了一个FormPanel
this.formPanel=new Ext.FormPanel({
defaults:{anchor:"95%",allowBlank:false},
labelWidth:55,
defaultType:"textfield",
items:[{
fieldLabel:"用户名",
name:"username"
},{
fieldLabel:"密码",
inputType:"password",
name:"password"
}]
});
//将表单列入到panel的items里去
this.add(this.formPanel);
}
});
然后我们可以将这个Panel包含在一个Window里面去,让他更像一个登陆窗口:
- //*测试
- Ext.onReady(function(){
- var panel=new Lesson1.FirstPanel({
- height:100,
- width:300,
- layout:"fit"
- });
- new Ext.Window({
- title:"登陆",
- items:[panel],
- buttons:[{
- text:"确定"
- },{
- text:"取消"
- }]
- }).show();
- });
- //*/
//*测试
Ext.onReady(function(){
var panel=new Lesson1.FirstPanel({
height:100,
width:300,
layout:"fit"
});
new Ext.Window({
title:"登陆",
items:[panel],
buttons:[{
text:"确定"
},{
text:"取消"
}]
}).show();
});
//*/
OK,到此为止,我们已经将这个登陆组件的大框架已经搭建好了,我们现在开始为我们的登录组件添加一个公开的函数让外部去调用:
- Ext.namespace("Lesson1.FirstPanel");
- /**
- * @author andy_ghg
- * @version 2009年9月24日23:22:09
- * @description 第一个派生类
- * @class Lesson1.FirstPanel
- * @extends Ext.Panel
- */
- Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
- frame:true,
- //初始化函数
- initComponent:function(){
- //继承父类的initComponent函数
- Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
- //为该组件添加了一个FormPanel
- this.formPanel=new Ext.FormPanel({
- defaults:{anchor:"95%",allowBlank:false},
- labelWidth:55,
- defaultType:"textfield",
- items:[{
- fieldLabel:"用户名",
- name:"username"
- },{
- fieldLabel:"密码",
- inputType:"password",
- name:"password"
- }]
- });
- this.add(this.formPanel);
- },
- // 提交表单的函数
- submitForm:function(){
- alert(this.formPanel);
- // this.formPanel.getForm().submit({
- // url:"",
- // success:function(){},
- // failure:function(){}
- // })
- }
- });
-
- //*测试
- Ext.onReady(function(){
- var panel=new Lesson1.FirstPanel({
- height:100,
- width:300,
- layout:"fit"
- });
- new Ext.Window({
- title:"登陆",
- items:[panel],
- buttons:[{
- text:"确定",
- handler:function(){
- panel.submitForm();
- }
- },{
- text:"取消"
- }]
- }).show();
- });
- //*/
Ext.namespace("Lesson1.FirstPanel");
/**
* @author andy_ghg
* @version 2009年9月24日23:22:09
* @description 第一个派生类
* @class Lesson1.FirstPanel
* @extends Ext.Panel
*/
Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
frame:true,
//初始化函数
initComponent:function(){
//继承父类的initComponent函数
Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
//为该组件添加了一个FormPanel
this.formPanel=new Ext.FormPanel({
defaults:{anchor:"95%",allowBlank:false},
labelWidth:55,
defaultType:"textfield",
items:[{
fieldLabel:"用户名",
name:"username"
},{
fieldLabel:"密码",
inputType:"password",
name:"password"
}]
});
this.add(this.formPanel);
},
// 提交表单的函数
submitForm:function(){
alert(this.formPanel);
// this.formPanel.getForm().submit({
// url:"",
// success:function(){},
// failure:function(){}
// })
}
});
//*测试
Ext.onReady(function(){
var panel=new Lesson1.FirstPanel({
height:100,
width:300,
layout:"fit"
});
new Ext.Window({
title:"登陆",
items:[panel],
buttons:[{
text:"确定",
handler:function(){
panel.submitForm();
}
},{
text:"取消"
}]
}).show();
});
//*/
至此,我不会再继续写下去了,我也不会提供源码,你可以充分发挥你的想象力去改造它,去感受一下这种写法的好处与优点,以及他即将会引发什么样的困惑.思考之后你才能有更深刻的了解.(你可以直接继承Window而不是继承Panel来做这个登录组件)
EXTJS组件化的共有和私有,上一篇文章中的组件如下:
- Ext.namespace("Lesson1.FirstPanel");
- /**
- * @author andy_ghg
- * @version 2009年9月24日23:22:09
- * @description 第一个派生类
- * @class Lesson1.FirstPanel
- * @extends Ext.Panel
- */
- Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
- frame:true,
- //初始化函数
- initComponent:function(){
- //继承父类的initComponent函数
- Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
- //为该组件添加了一个FormPanel
- this.formPanel=new Ext.FormPanel({
- defaults:{anchor:"95%",allowBlank:false},
- labelWidth:55,
- defaultType:"textfield",
- items:[{
- fieldLabel:"用户名",
- name:"username"
- },{
- fieldLabel:"密码",
- inputType:"password",
- name:"password"
- }]
- });
- this.add(this.formPanel);
- },
- submitForm:function(){
- alert(this.formPanel);
- // this.formPanel.getForm().submit({
- // url:"",
- // success:function(){},
- // failure:function(){}
- // })
- }
- });
Ext.namespace("Lesson1.FirstPanel");
/**
* @author andy_ghg
* @version 2009年9月24日23:22:09
* @description 第一个派生类
* @class Lesson1.FirstPanel
* @extends Ext.Panel
*/
Lesson1.FirstPanel=Ext.extend(Ext.Panel,{
frame:true,
//初始化函数
initComponent:function(){
//继承父类的initComponent函数
Lesson1.FirstPanel.superclass.initComponent.call(this,arguments);
//为该组件添加了一个FormPanel
this.formPanel=new Ext.FormPanel({
defaults:{anchor:"95%",allowBlank:false},
labelWidth:55,
defaultType:"textfield",
items:[{
fieldLabel:"用户名",
name:"username"
},{
fieldLabel:"密码",
inputType:"password",
name:"password"
}]
});
this.add(this.formPanel);
},
submitForm:function(){
alert(this.formPanel);
// this.formPanel.getForm().submit({
// url:"",
// success:function(){},
// failure:function(){}
// })
}
});
我们可以看到,组件中所有的部件都是通过this.的方式来声明的,这样做有一个好处,我们可以直接通过组件的实例化对象去访问这些组成部分,例如:
- //*测试
- Ext.onReady(function(){
- var panel=new Lesson1.FirstPanel({
- height:100,
- width:300,
- renderTo:Ext.getBody(),
- title:"组件",
- layout:"fit"
- });
- alert(panel.formPanel);
- });
- //*/
//*测试
Ext.onReady(function(){
var panel=new Lesson1.FirstPanel({
height:100,
width:300,
renderTo:Ext.getBody(),
title:"组件",
layout:"fit"
});
alert(panel.formPanel);
});
//*/
这里弹出的是一个Object,说明我们成功访问了组件内部的成员,这样做的好处很多,我们可以通过实例化对象来获取它内部的成员,从而操作它内部的成员.
但是,我们在很多情况下是不愿意让别人去调用里面的东西的,怎么办?可以使用var关键字来创建组件内部私有的成员,这样,外部就不可以通过实例化对象去访问它们了:
- Ext.namespace("Lesson2.FirstPanel");
- /**
- * @author andy_ghg
- * @version 2009年9月25日20:30:15
- * @description 私有与公有
- * @class Lesson2.FirstPanel
- * @extends Ext.Panel
- */
- Lesson2.FirstPanel = Ext.extend(Ext.Panel,{
- frame : true,
- //初始化函数
- initComponent : function(){
- var privateObj = new Ext.Panel();
- this.publicObj = new Ext.Panel();
- }
- });
-
- //*测试
- Ext.onReady(function(){
- var panel = new Lesson2.FirstPanel({
- height:100,
- width:300,
- renderTo:Ext.getBody(),
- title:"组件",
- layout:"fit"
- });
-
- alert(panel.privateObj);
- alert(panel.publicObj);
- });
- //*/
Ext.namespace("Lesson2.FirstPanel");
/**
* @author andy_ghg
* @version 2009年9月25日20:30:15
* @description 私有与公有
* @class Lesson2.FirstPanel
* @extends Ext.Panel
*/
Lesson2.FirstPanel = Ext.extend(Ext.Panel,{
frame : true,
//初始化函数
initComponent : function(){
var privateObj = new Ext.Panel();
this.publicObj = new Ext.Panel();
}
});
//*测试
Ext.onReady(function(){
var panel = new Lesson2.FirstPanel({
height:100,
width:300,
renderTo:Ext.getBody(),
title:"组件",
layout:"fit"
});
alert(panel.privateObj);
alert(panel.publicObj);
});
//*/
第一次弹出的是undifined第二次弹出的是Object,其实这个非常容易理解,上面的代码privateObj是作为一个对象存在的,而publicObj则是作为一个Lesson2.FirstPane的成员存在的,自然,publicObj就可以通过实例化对象被访问到,而privateObj由于作用域的不同,自然就无法被外部函数访问到.
其实这种私有对象的写法没有太大用处,我一般是将函数作为私有来用的,而对象则没有必要私有化:
- Ext.namespace("Lesson2.FirstPanel");
- /**
- * @author andy_ghg
- * @version 2009年9月24日23:22:09
- * @description 私有与公有
- * @class Lesson2.FirstPanel
- * @extends Ext.Panel
- */
- Lesson2.FirstPanel = Ext.extend(Ext.Panel,{
- frame : true,
- //初始化函数
- initComponent : function(){
- Lesson2.FirstPanel.superclass.initComponent.call(this,arguments);
- var hello=function(){
- //我是私有函数,只有内部成员才能访问到
- alert("private");
- }
- this.publicFn=function(){
- //我是共有函数,外部可以通过对象名来调用
- alert("public");
- }
- this.buttons=[{
- text:"调用私有",
- handler:hello
- },{
- text:"调用公有",
- handler:this.publicFn,
- scope:this
- }]
- }
- });
Ext.namespace("Lesson2.FirstPanel");
/**
* @author andy_ghg
* @version 2009年9月24日23:22:09
* @description 私有与公有
* @class Lesson2.FirstPanel
* @extends Ext.Panel
*/
Lesson2.FirstPanel = Ext.extend(Ext.Panel,{
frame : true,
//初始化函数
initComponent : function(){
Lesson2.FirstPanel.superclass.initComponent.call(this,arguments);
var hello=function(){
//我是私有函数,只有内部成员才能访问到
alert("private");
}
this.publicFn=function(){
//我是共有函数,外部可以通过对象名来调用
alert("public");
}
this.buttons=[{
text:"调用私有",
handler:hello
},{
text:"调用公有",
handler:this.publicFn,
scope:this
}]
}
});
公开的函数可以不必定义在初始化方法里,一般都是定义在初始化方法外面的,就例如上面第一段代码中的submitForm函数一样.
在使用这种写法的时候要注意一个问题,就是,定义组件内部的成员的时候,是通过this.的方式去定义的,但是如果组件自身就有这个属性就会造成重写,比如下面的代码将不会显示title里的文字:
- Ext.namespace("Lesson2.FirstPanel");
- /**
- * @author andy_ghg
- * @version 2009年9月24日23:22:09
- * @description 私有与公有
- * @class Lesson2.FirstPanel
- * @extends Ext.Panel
- */
- Lesson2.FirstPanel = Ext.extend(Ext.Panel,{
- frame : true,
- //初始化函数
- initComponent : function(){
- Lesson2.FirstPanel.superclass.initComponent.call(this,arguments);
- this.title=null;
- }
- });
-
- //*测试
- Ext.onReady(function(){
- var panel = new Lesson2.FirstPanel({
- height:100,
- width:300,
- renderTo:Ext.getBody(),
- title:"组件",
- layout:"fit"
- });
- panel.publicFn();
- });
- //*/
Ext.namespace("Lesson2.FirstPanel");
/**
* @author andy_ghg
* @version 2009年9月24日23:22:09
* @description 私有与公有
* @class Lesson2.FirstPanel
* @extends Ext.Panel
*/
Lesson2.FirstPanel = Ext.extend(Ext.Panel,{
frame : true,
//初始化函数
initComponent : function(){
Lesson2.FirstPanel.superclass.initComponent.call(this,arguments);
this.title=null;
}
});
//*测试
Ext.onReady(function(){
var panel = new Lesson2.FirstPanel({
height:100,
width:300,
renderTo:Ext.getBody(),
title:"组件",
layout:"fit"
});
panel.publicFn();
});
//*/
我们可以看到设置的title没有被展现出来,所以,当我们在新定义一个组件内部成员的时候一定要避免与组件父类里的成员冲突(除非你是刻意要重写),以免造成不必要的麻烦.这里就要考验你对你所继承的父类的认知程度了.
在开发过程中,当许多小组件拼合成一个大组件之后,最先遇到的问题就是组件与组件之间的通信和数据交互.
如果你的组件封装的比较死(即在创建的时候不需要配置属性).则可以通过组建的自定义事件来完成组件与组件之间的项目调用.下面的界面其实没有必要这么做,这么做的目的只是为了解释一下组件与组件之间相互交互数据:
- //Panel1和Panel2为视图组件,Main为容器组件
- Ext.namespace("Lesson2.Panel1");
- /**
- * @author andy_ghg
- * @version 2009年10月17日1:36:26
- * @description 组件之间的数据交互(Grid)
- * @class Lesson2.Panel1
- * @extends Ext.Panel
- */
- Lesson2.Panel1 = Ext.extend(Ext.Panel,{
- layout:"fit",
- height:200,
- //初始化函数
- initComponent : function(){
- Lesson2.Panel1.superclass.initComponent.call(this,arguments);
- this.addEvents("gridRowSelected");
- this.gridStore = new Ext.data.JsonStore({
- url:"",
- fields:["xx","yy"],
- totalPropery:"results",
- root:"items"
- });
- this.gridSm = new Ext.grid.CheckboxSelectionModel();
-
- this.gridCm = new Ext.grid.ColumnModel([this.gridSm,{
- header:"列一",
- dataIndex:"xx"
- },{
- header:"列二",
- dataIndex:"yy"
- }]);
-
- this.gridPanel = new Ext.grid.GridPanel({
- sm:this.gridSm,
- cm:this.gridCm,
- store:this.gridStore,
- viewConfig:{
- autoFill:true,
- forceFit:true
- }
- });
- this.gridPanel.on("rowclick",this.rowSelect,this);
- this.add(this.gridPanel);
- },
- //提供给外部调用的函数,返回其内部的store
- getStore:function(){
- return this.gridPanel.getStore();
- },
- rowSelect:function(grid,index,e){
- var record = grid.getStore().getAt(index);
- this.fireEvent("gridRowSelected",record);
- }
- });
- Ext.namespace("Lesson2.Panel2");
- /**
- * @description 组件之间的相互交互(formPanel)
- * @class Lesson2.Panel2
- * @extends Ext.Panel
- */
- Lesson2.Panel2 = Ext.extend(Ext.Panel,{
- layout:"fit",
- frame:true,
- initComponent:function(){
- Lesson2.Panel2.superclass.initComponent.call(this,arguments);
- this.addEvents("addRecord");
- this.formPanel = new Ext.FormPanel({
- defaults:{anchor:"95%"},
- defaultType:"textfield",
- labelWidth:55,
- items:[{
- fieldLabel:"XXXXX",
- name:"xx"
- },{
- fieldLabel:"YYYYY",
- name:"yy"
- }]
- });
- this.add(this.formPanel);
- this.addButton("加入",this.addRecord,this);
- },
- //触发自定义事件,并向事件中传递一个参数values
- addRecord:function(){
- var values = this.formPanel.getForm().getValues();
- this.fireEvent("addRecord",values);
- }
- });
-
- Ext.namespace("Lesson2.Main");
- /**
- * @description 用于将两个子组件拼合在一起的容器
- * @class Lesson2.Main
- * @extends Ext.Panel
- */
- Lesson2.Main = Ext.extend(Ext.Panel,{
- renderTo:Ext.getBody(),
- layout:"form",
- initComponent:function(){
- Lesson2.Main.superclass.initComponent.call(this,arguments);
- this.panel1 = new Lesson2.Panel1();
- this.panel2 = new Lesson2.Panel2();
- //在这里捕获panel2的自定义事件
- this.panel2.on("addRecord",this.addRecordToGrid,this);
- this.panel1.on("gridRowSelected",this.addRecordToForm,this);
- //将两个组件加入到视图中去
- this.add(this.panel1);
- this.add(this.panel2);
- },
- //TODO panel2的事件处理函数,在这里的this代表Lesson2.Main
- //这里通过this获取panel1的实例,再通过panel1的实例调用panel1的方法getStore()
- //panel1的getStore()函数会返回其内部的gridPanel的Store
- //参数的values就是panel2触发了自定义事件后传递进来的
- addRecordToGrid:function(values){
- var record = new Ext.data.Record(values);
- this.panel1.getStore().add(record);
- },
- //TODO 第二种方法,直接获取panel1里的store,效果是一样的
- addRecordToGrid_2:function(values){
- var record = new Ext.data.Record(values);
- this.panel1.gridStore.add(record);
- },
- //TODO panel1的事件处理函数,在这里会获取到panel2的实例,并通过该实例获取其内部的formPanel并调用
- //formPanel的相应方法来达到读取数据的目的
- addRecordToForm:function(record){
- this.panel2.formPanel.getForm().loadRecord(record);
- }
- });
- Ext.onReady(function(){
- var ls = new Lesson2.Main({
- title:"测试",
- width:400
- });
- });
//Panel1和Panel2为视图组件,Main为容器组件
Ext.namespace("Lesson2.Panel1");
/**
* @author andy_ghg
* @version 2009年10月17日1:36:26
* @description 组件之间的数据交互(Grid)
* @class Lesson2.Panel1
* @extends Ext.Panel
*/
Lesson2.Panel1 = Ext.extend(Ext.Panel,{
layout:"fit",
height:200,
//初始化函数
initComponent : function(){
Lesson2.Panel1.superclass.initComponent.call(this,arguments);
this.addEvents("gridRowSelected");
this.gridStore = new Ext.data.JsonStore({
url:"",
fields:["xx","yy"],
totalPropery:"results",
root:"items"
});
this.gridSm = new Ext.grid.CheckboxSelectionModel();
this.gridCm = new Ext.grid.ColumnModel([this.gridSm,{
header:"列一",
dataIndex:"xx"
},{
header:"列二",
dataIndex:"yy"
}]);
this.gridPanel = new Ext.grid.GridPanel({
sm:this.gridSm,
cm:this.gridCm,
store:this.gridStore,
viewConfig:{
autoFill:true,
forceFit:true
}
});
this.gridPanel.on("rowclick",this.rowSelect,this);
this.add(this.gridPanel);
},
//提供给外部调用的函数,返回其内部的store
getStore:function(){
return this.gridPanel.getStore();
},
rowSelect:function(grid,index,e){
var record = grid.getStore().getAt(index);
this.fireEvent("gridRowSelected",record);
}
});
Ext.namespace("Lesson2.Panel2");
/**
* @description 组件之间的相互交互(formPanel)
* @class Lesson2.Panel2
* @extends Ext.Panel
*/
Lesson2.Panel2 = Ext.extend(Ext.Panel,{
layout:"fit",
frame:true,
initComponent:function(){
Lesson2.Panel2.superclass.initComponent.call(this,arguments);
this.addEvents("addRecord");
this.formPanel = new Ext.FormPanel({
defaults:{anchor:"95%"},
defaultType:"textfield",
labelWidth:55,
items:[{
fieldLabel:"XXXXX",
name:"xx"
},{
fieldLabel:"YYYYY",
name:"yy"
}]
});
this.add(this.formPanel);
this.addButton("加入",this.addRecord,this);
},
//触发自定义事件,并向事件中传递一个参数values
addRecord:function(){
var values = this.formPanel.getForm().getValues();
this.fireEvent("addRecord",values);
}
});
Ext.namespace("Lesson2.Main");
/**
* @description 用于将两个子组件拼合在一起的容器
* @class Lesson2.Main
* @extends Ext.Panel
*/
Lesson2.Main = Ext.extend(Ext.Panel,{
renderTo:Ext.getBody(),
layout:"form",
initComponent:function(){
Lesson2.Main.superclass.initComponent.call(this,arguments);
this.panel1 = new Lesson2.Panel1();
this.panel2 = new Lesson2.Panel2();
//在这里捕获panel2的自定义事件
this.panel2.on("addRecord",this.addRecordToGrid,this);
this.panel1.on("gridRowSelected",this.addRecordToForm,this);
//将两个组件加入到视图中去
this.add(this.panel1);
this.add(this.panel2);
},
//TODO panel2的事件处理函数,在这里的this代表Lesson2.Main
//这里通过this获取panel1的实例,再通过panel1的实例调用panel1的方法getStore()
//panel1的getStore()函数会返回其内部的gridPanel的Store
//参数的values就是panel2触发了自定义事件后传递进来的
addRecordToGrid:function(values){
var record = new Ext.data.Record(values);
this.panel1.getStore().add(record);
},
//TODO 第二种方法,直接获取panel1里的store,效果是一样的
addRecordToGrid_2:function(values){
var record = new Ext.data.Record(values);
this.panel1.gridStore.add(record);
},
//TODO panel1的事件处理函数,在这里会获取到panel2的实例,并通过该实例获取其内部的formPanel并调用
//formPanel的相应方法来达到读取数据的目的
addRecordToForm:function(record){
this.panel2.formPanel.getForm().loadRecord(record);
}
});
Ext.onReady(function(){
var ls = new Lesson2.Main({
title:"测试",
width:400
});
});
如果你的组件封装的比较灵活,则可以在容器内就直接调用容器中的方法进行操作,比如上面的代码稍微修改一下(注意panel2的Button):
- Ext.namespace("Lesson2.Panel1");
- /**
- * @author andy_ghg
- * @version 2009年10月17日1:36:26
- * @description 组件之间的数据交互(Grid)
- * @class Lesson2.Panel1
- * @extends Ext.Panel
- */
- Lesson2.Panel1 = Ext.extend(Ext.Panel,{
- layout:"fit",
- height:200,
- //初始化函数
- initComponent : function(){
- Lesson2.Panel1.superclass.initComponent.call(this,arguments);
- this.addEvents("gridRowSelected");
- this.gridStore = new Ext.data.JsonStore({
- url:"",
- fields:["xx","yy"],
- totalPropery:"results",
- root:"items"
- });
- this.gridSm = new Ext.grid.CheckboxSelectionModel();
-
- this.gridCm = new Ext.grid.ColumnModel([this.gridSm,{
- header:"列一",
- dataIndex:"xx"
- },{
- header:"列二",
- dataIndex:"yy"
- }]);
-
- this.gridPanel = new Ext.grid.GridPanel({
- sm:this.gridSm,
- cm:this.gridCm,
- store:this.gridStore,
- viewConfig:{
- autoFill:true,
- forceFit:true
- }
- });
- this.gridPanel.on("rowclick",this.rowSelect,this);
- this.add(this.gridPanel);
- },
- //提供给外部调用的函数,返回其内部的store
- getStore:function(){
- return this.gridPanel.getStore();
- },
- rowSelect:function(grid,index,e){
- var record = grid.getStore().getAt(index);
- this.fireEvent("gridRowSelected",record);
- }
- });
- Ext.namespace("Lesson2.Panel2");
- /**
- * @description 组件之间的相互交互(formPanel)
- * @class Lesson2.Panel2
- * @extends Ext.Panel
- */
- Lesson2.Panel2 = Ext.extend(Ext.Panel,{
- layout:"fit",
- frame:true,
- initComponent:function(){
- Lesson2.Panel2.superclass.initComponent.call(this,arguments);
- this.formPanel = new Ext.FormPanel({
- defaults:{anchor:"95%"},
- defaultType:"textfield",
- labelWidth:55,
- items:[{
- fieldLabel:"XXXXX",
- name:"xx"
- },{
- fieldLabel:"YYYYY",
- name:"yy"
- }]
- });
- this.add(this.formPanel);
- }
- });
-
- Ext.namespace("Lesson2.Main");
- /**
- * @description 用于将两个子组件拼合在一起的容器
- * @class Lesson2.Main
- * @extends Ext.Panel
- */
- Lesson2.Main = Ext.extend(Ext.Panel,{
- renderTo:Ext.getBody(),
- layout:"form",
- initComponent:function(){
- Lesson2.Main.superclass.initComponent.call(this,arguments);
- this.panel1 = new Lesson2.Panel1();
- this.panel2 = new Lesson2.Panel2({
- buttons:[{
- text:"确定",
- handler:this.addRecordToGrid_2,
- scope:this
- }]
- });
- //在这里捕获panel2的自定义事件
- this.panel1.on("gridRowSelected",this.addRecordToForm,this);
- //将两个组件加入到视图中去
- this.add(this.panel1);
- this.add(this.panel2);
- },
- //这里直接就获取当前容器的子组件panel2并获取panel2中的formPanel进行操作
- addRecordToGrid_2:function(){
- var values = this.panel2.formPanel.getForm().getValues();
- var record = new Ext.data.Record(values);
- this.panel1.gridStore.add(record);
- },
- //TODO panel1的事件处理函数,在这里会获取到panel2的实例,并通过该实例获取其内部的formPanel并调用
- //formPanel的相应方法来达到读取数据的目的
- addRecordToForm:function(record){
- this.panel2.formPanel.getForm().loadRecord(record);
- }
- });
- Ext.onReady(function(){
- var ls = new Lesson2.Main({
- title:"测试",
- width:400
- });
- });
减少代码量的编写自然是对象的重复利用,以前所写的组件化,它确实是一个独立的对象,可以拿来重用,但是,写到最后会发现,这些组件往往只是使用了一次而已,而且根据需求的不同会发现这个组件根本就无法被重新使用了(定制性太强)
于是,我们就需要拆分这个组件,看看这些组件中都有哪些东西是经常被用到的.
比如导航栏,一排导航十多个按钮,他们都是按钮,但是他们的handler虽然都是切换显示页面,但是每个按钮所负责的页面却是不同的,曾经有人问过,我是不是要为每一个按钮都编写一个handler处理函数?
- Ext.onReady(function(){
- var panel = new Ext.Panel({
- renderTo:Ext.getBody(),
- border:false,
- tbar: [{
- xtype: 'buttongroup',
- columns: 6,
- title: '客户管理',
- // defaults:{xtype:"xmenubutton"},
- items: [{
- text: '客户信息',
- scale: 'large',
- rowspan: 2,
- handler:function(){
- //TODO 处理
- },
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- },{
- text: '新增客户',
- scale: 'large',
- rowspan: 2,
- handler:function(){
- //TODO 处理
- },
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- },{
- text: '客户通讯录',
- scale: 'large',
- rowspan: 2,
- handler:function(){
- //TODO 处理
- },
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- }]
- }]
- });
- });
Ext.onReady(function(){
var panel = new Ext.Panel({
renderTo:Ext.getBody(),
border:false,
tbar: [{
xtype: 'buttongroup',
columns: 6,
title: '客户管理',
// defaults:{xtype:"xmenubutton"},
items: [{
text: '客户信息',
scale: 'large',
rowspan: 2,
handler:function(){
//TODO 处理
},
iconCls: 'menuDefault_32',
iconAlign: 'top'
},{
text: '新增客户',
scale: 'large',
rowspan: 2,
handler:function(){
//TODO 处理
},
iconCls: 'menuDefault_32',
iconAlign: 'top'
},{
text: '客户通讯录',
scale: 'large',
rowspan: 2,
handler:function(){
//TODO 处理
},
iconCls: 'menuDefault_32',
iconAlign: 'top'
}]
}]
});
});
这样写起来,一旦导航多了,就比较麻烦了,代码显得很乱,不好维护,怎么办?
也许有人会这么去写它:
- Ext.onReady(function(){
- function onBtnClickHandler(btn){
- switch (btn.text){
- case "客户信息":
- //TODO
- alert("aaa");
- break;
- case "新增客户":
- //TODO
- break;
- case "客户通讯录":
- //TODO
- break;
- }
- }
- var panel = new Ext.Panel({
- renderTo:Ext.getBody(),
- border:false,
- tbar: [{
- xtype: 'buttongroup',
- columns: 6,
- title: '客户管理',
- items: [{
- text: '客户信息',
- scale: 'large',
- rowspan: 2,
- handler:onBtnClickHandler,
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- },{
- text: '新增客户',
- scale: 'large',
- rowspan: 2,
- handler:onBtnClickHandler,
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- },{
- text: '客户通讯录',
- scale: 'large',
- rowspan: 2,
- handler:onBtnClickHandler,
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- }]
- }]
- });
- });
Ext.onReady(function(){
function onBtnClickHandler(btn){
switch (btn.text){
case "客户信息":
//TODO
alert("aaa");
break;
case "新增客户":
//TODO
break;
case "客户通讯录":
//TODO
break;
}
}
var panel = new Ext.Panel({
renderTo:Ext.getBody(),
border:false,
tbar: [{
xtype: 'buttongroup',
columns: 6,
title: '客户管理',
items: [{
text: '客户信息',
scale: 'large',
rowspan: 2,
handler:onBtnClickHandler,
iconCls: 'menuDefault_32',
iconAlign: 'top'
},{
text: '新增客户',
scale: 'large',
rowspan: 2,
handler:onBtnClickHandler,
iconCls: 'menuDefault_32',
iconAlign: 'top'
},{
text: '客户通讯录',
scale: 'large',
rowspan: 2,
handler:onBtnClickHandler,
iconCls: 'menuDefault_32',
iconAlign: 'top'
}]
}]
});
});
这样看起来就好很多,实现了业务与现实的分离,但是按钮多了,还是很郁闷,要写很多switch/case,
如何省去这些步骤呢?
我们发现他们的handler有一个共同点就是,都是负责切换显示页面的(也可能是弹出Window).
那么有这点相同就可以了,我直接就贴上来我的处理方法吧:
先写一个全局的对象,用于处理这些按钮如下:
- Ext.namespace("Crm.Control.ConstEvent");
- /**
- * 核心控制器
- * @type
- */
- Crm.Control.ConstEvent = {
- isInit:false,
- //初始化,主要是为该对象提供一个可以切换的容器,相当于Iframe
- init:function(panel){
- this.panel = panel;//当做IFrame来使
- this.isInit = true;
- },
- /**
- * 切换主容器显示的内容函数(就是初始化传递进来的Panel)
- * @param {} obj 一个字符串(具体是什么字符串请看下面的例子)
- */
- changePanel:function(obj){
- //如果已经初始化
- if (this.isInit) {
- //由于时间关系,我这里就不判断是否已经存在相同的对象了,按理来讲应该判断一下传递进来的obj是否与
- //当前显示的obj是一个东西,如果是一个东西则return,至于如何判断,还是等下次再说吧
- try{
- this.panel.removeAll();//先移除先前加载进来的Panel(注意释放内存,此处省略)
- }catch(e){
- //EXTJS自带报表切换时会出现异常,具体原因不明,仅在IE下会出现此异常
- }finally{
- this.panel.add(eval(obj));//eval menuButton传递进来的对象(实际是个字符串具体看下面的例子)
- this.panel.doLayout();//调用布局函数,这样才会显示你刚刚添加进来的组件
- }
- }
- },
- /**
- * 如果按钮指向的对象是window则使用此函数
- * @param {} obj 一个字符串
- */
- showWindow:function(obj){
- if (Ext.getCmp(obj.id)) {
- Ext.getCmp(obj.id).show();
- return;
- }
- if (this.isInit) {
- if(Ext.getCmp(obj.substring(4,obj.length-2))){
- Ext.getCmp(obj.substring(4,obj.length-2)).show();
- return;
- }
- eval(obj).show();
- }
- }
- };
Ext.namespace("Crm.Control.ConstEvent");
/**
* 核心控制器
* @type
*/
Crm.Control.ConstEvent = {
isInit:false,
//初始化,主要是为该对象提供一个可以切换的容器,相当于Iframe
init:function(panel){
this.panel = panel;//当做IFrame来使
this.isInit = true;
},
/**
* 切换主容器显示的内容函数(就是初始化传递进来的Panel)
* @param {} obj 一个字符串(具体是什么字符串请看下面的例子)
*/
changePanel:function(obj){
//如果已经初始化
if (this.isInit) {
//由于时间关系,我这里就不判断是否已经存在相同的对象了,按理来讲应该判断一下传递进来的obj是否与
//当前显示的obj是一个东西,如果是一个东西则return,至于如何判断,还是等下次再说吧
try{
this.panel.removeAll();//先移除先前加载进来的Panel(注意释放内存,此处省略)
}catch(e){
//EXTJS自带报表切换时会出现异常,具体原因不明,仅在IE下会出现此异常
}finally{
this.panel.add(eval(obj));//eval menuButton传递进来的对象(实际是个字符串具体看下面的例子)
this.panel.doLayout();//调用布局函数,这样才会显示你刚刚添加进来的组件
}
}
},
/**
* 如果按钮指向的对象是window则使用此函数
* @param {} obj 一个字符串
*/
showWindow:function(obj){
if (Ext.getCmp(obj.id)) {
Ext.getCmp(obj.id).show();
return;
}
if (this.isInit) {
if(Ext.getCmp(obj.substring(4,obj.length-2))){
Ext.getCmp(obj.substring(4,obj.length-2)).show();
return;
}
eval(obj).show();
}
}
};
下面是所有MenuButton的父类:
- Ext.namespace("Ext.ux.MenuButton.Button","Ext.ux.MenuButton.SplitButton");
- Ext.ux.MenuButton.Button = Ext.extend(Ext.Button,{
- pageObject:"",//此按钮将要指向的那个模块(例如"new AAA.bbb.ccc()")
- isWindow:false,//此按钮指向的那个模块是否是一个window
- handler:function(btn){
- if (btn.isWindow) {
- Crm.Control.ConstEvent.showWindow(btn.pageObject);//如果按钮所包含的实体类是个window,则调用window展示函数
- return;
- }
- if (this.pageObject!="") {
- Crm.Control.ConstEvent.changePanel(btn.pageObject);//如果按钮所包含的实体类是个页面,则调用展示Panel的函数
- }
- }
- });
- Ext.reg("xmenubutton",Ext.ux.MenuButton.Button);
- Ext.ux.MenuButton.SplitButton = Ext.extend(Ext.SplitButton,{
- pageObject:"",
- handler:function(btn){
- if (btn.isWindow) {
- Crm.Control.ConstEvent.showWindow(btn.pageObject);
- return;
- }
- if (this.pageObject!="") {
- Crm.Control.ConstEvent.changePanel(btn.pageObject);
- }
- }
- });
- Ext.reg("xsplitbutton",Ext.ux.MenuButton.SplitButton);
Ext.namespace("Ext.ux.MenuButton.Button","Ext.ux.MenuButton.SplitButton");
Ext.ux.MenuButton.Button = Ext.extend(Ext.Button,{
pageObject:"",//此按钮将要指向的那个模块(例如"new AAA.bbb.ccc()")
isWindow:false,//此按钮指向的那个模块是否是一个window
handler:function(btn){
if (btn.isWindow) {
Crm.Control.ConstEvent.showWindow(btn.pageObject);//如果按钮所包含的实体类是个window,则调用window展示函数
return;
}
if (this.pageObject!="") {
Crm.Control.ConstEvent.changePanel(btn.pageObject);//如果按钮所包含的实体类是个页面,则调用展示Panel的函数
}
}
});
Ext.reg("xmenubutton",Ext.ux.MenuButton.Button);
Ext.ux.MenuButton.SplitButton = Ext.extend(Ext.SplitButton,{
pageObject:"",
handler:function(btn){
if (btn.isWindow) {
Crm.Control.ConstEvent.showWindow(btn.pageObject);
return;
}
if (this.pageObject!="") {
Crm.Control.ConstEvent.changePanel(btn.pageObject);
}
}
});
Ext.reg("xsplitbutton",Ext.ux.MenuButton.SplitButton);
那么我们最上面的Menu就可以这么去写它
- var panel = new Ext.Panel({
- renderTo:Ext.getBody(),
- border:false,
- tbar: [{
- xtype: 'buttongroup',
- columns: 6,
- defaults:{xtype:"xmenubutton"},
- title: '客户管理',
- items: [{
- text: '客户信息',
- scale: 'large',
- rowspan: 2,
- pageObject:"new Crm.Module.Client.BaseInfo()",//一个对象,用于传递给所谓的"核心控制器",就是上面的全局变量
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- },{
- text: '新增客户',
- scale: 'large',
- rowspan: 2,
- isWindow:true,
- pageObject:"new Crm.Module.Client.NewClient()",
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- },{
- text: '客户通讯录',
- scale: 'large',
- rowspan: 2,
- pageObject:"new Crm.Module.Client.AddList()",
- iconCls: 'menuDefault_32',
- iconAlign: 'top'
- }]
- }]
- });
var panel = new Ext.Panel({
renderTo:Ext.getBody(),
border:false,
tbar: [{
xtype: 'buttongroup',
columns: 6,
defaults:{xtype:"xmenubutton"},
title: '客户管理',
items: [{
text: '客户信息',
scale: 'large',
rowspan: 2,
pageObject:"new Crm.Module.Client.BaseInfo()",//一个对象,用于传递给所谓的"核心控制器",就是上面的全局变量
iconCls: 'menuDefault_32',
iconAlign: 'top'
},{
text: '新增客户',
scale: 'large',
rowspan: 2,
isWindow:true,
pageObject:"new Crm.Module.Client.NewClient()",
iconCls: 'menuDefault_32',
iconAlign: 'top'
},{
text: '客户通讯录',
scale: 'large',
rowspan: 2,
pageObject:"new Crm.Module.Client.AddList()",
iconCls: 'menuDefault_32',
iconAlign: 'top'
}]
}]
});
这样一来,我们就可以少写很多代码了.