大规模客户端应用一般很差实现很差组织也很差维护,由于功能和人力的不断增长,这些应用的规模很快就会超出掌控能力,ExtJS4带来了一个新的应用架构,不但能够组织代码,还能够减小实现的内容。css
新的应用架构遵守一个类MVC的模式,模型(Models)和控制器(Controllers)首次被引入。业界有不少种MVC架构,基本大同小异,ExtJS4的定义以下:html
a.Model模型:模型是字段和它们的数据的集合,例如User模型带有username和password字段,模型知道如何持久化本身的数据,而且能够和其余模型关联,模型跟ExtJS 3 中的Record类有点像(区别是,Record只是单纯的扁平结构,而Model能够nest),一般都用在Store中去展现grid和其余组件的数据。ajax
b.View视图:视图是组件的一种,专一于界面展现 – grid, tree, panel 都是view。数据库
c.Controllers控制器:一个安放全部使你的app正确工做的代码的位置,具体一点应该是全部动做,例如如何渲染view,如何初始化model,和app的其余逻辑。json
请注意:MVC是一个框架,不是设计模式,更多的内容请参考: 百度百科设计模式
框架与设计模式虽然类似,但却有着根本的不一样。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述,它比框架更抽象;框架能够用代码表示,也能直接执行或复用,而对模式而言只有实例才能用代码表示;设计模式是比框架更小的元素,一个框架中每每含有一个或多个设计模式,框架老是针对某一特定应用领域,但同一模式却可适用于各类应用。能够说,框架是软件,而设计模式是软件的知识。api
简而言之:设计模式是大智慧,用来对软件设计进行分工;框架模式是小技巧,对具体问题提出解决方案,以提升代码复用率,下降耦合度。数组
ExtJS 4 应用都遵循一个统一的目录结构,每一个应有都相同 MVC中,全部类都放在app
目录里面,这个目录能够有子目录,表明的是命名空间(一个子目录对应一个命名空间),使用不一样的目录存放views
,models
,controllers
,stores
。当咱们完成例子的时候,目录结构应该和下图同样:服务器
ExtJS SDK必须的文件在目录ext4中,所以,index.html
应该引入extjs必须的js和css,以及app.js文件 架构
每一个ExtJS 4的应用都从一个Application
类的实例开始,这个实例包含应用的全局配置(例如应用的名字),这个实例也负责维护对所有模型、视图、控制器的引用的维护,还有一个launch
函数,会在全部加载项加载完成以后调用。
首先须要选择一个全局命名空间,全部ExtJS4应用都须要有一个全局命名空间,以让全部应用中的类安放到其中:
Ext.application({ requires: ['Ext.container.Viewport'], name: 'FWY',//定义的命名空间 appFolder: 'app',//指明应用的根目录 launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: [ { xtype: 'panel', title: '标题', html : '内容' } ] }); } });
控制器是应用的粘合剂,它们所做的事情就是监听事件并执行动做,继续咱们的应用,建立一个控制器。建立app/controller/Students.js
这个文件,并添加以下代码:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', init: function() { console.debug("trigger controller init event"); } });
接下来在app.js
中添加对Students控制器的引用:
Ext.application({ ... controllers: [ 'Students' //对应于controller文件夹下面的Students.js ], ... });
当咱们经过index.html查看应用,Students控制器会被自动加载(由于在app.js的Application中增长了引用),而且Students的init方法会在launch以前调用。
init
方法是个极好的地方,能够用来设置如何和view交互,一般都使用Controller的一个方法control,control方法使得监听view的事件变的容易,更新一下控制器,让它告知咱们panel什么时候渲染:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', init: function() { this.control({ 'viewport > panel': { render: this.onPanelRendered } }); }, onPanelRendered: function() { console.debug('该panel被渲染了'); } });
咱们已经更新了init方法,使用this.controll给视图设置监听器。这个controll方法,使用最新的组件查询引擎(ComponentQuery)能够快速方便的找到页面上的组件。若是你对ComponentQuery不熟悉,能够查看ComponentQuery文档进行详细了解。简要一点,ComponentQuery能够容许咱们使用一个相似css选择器的方式找到组件。
在例子的init方法中咱们应用了'viewport > panel',能够解释为“查找Viewport直接后代中的全部Panel组件”,而后咱们提供了一个对象匹配事件名称(这个例子中只用了render)来提供响应函数。所有的影响就是不管哪一个组件符合咱们的选择器,当它的render事件触发时,咱们的onPanelRendered函数都会被调用。
直到如今,咱们的应用只有不多代码,只有两个文 件 app.js 和 app/controller/Students.js,如今咱们想增长一个grid显示全部系统中的学生列表,修改3处:
是时候更好的组织一下逻辑并开始使用视图了。
视图也是组件,一般都是ExtJS现有组件的子类,如今准备建立学生表,先建立 app/view/student/List.js
,添加代码:
Ext.define('FWY.view.student.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.studentlist', title : '学生信息列表', initComponent: function() { this.store = { fields: ['id','name', 'age','sex'], data : [ {id:1,name: 'zhangsan', age: 18,sex:'boy'}, {id:2,name: 'lishi', age: 20,sex:'girl'} ]}; this.columns = [ {header: '编号', dataIndex: 'id', flex: 1}, {header: '姓名', dataIndex: 'name', flex: 1}, {header: '年龄', dataIndex: 'age', flex: 1}, {header: '性别', dataIndex: 'sex', flex: 1} ]; this.callParent(arguments); } });
咱们的视图类就是一个普通的类,这个例子中咱们扩展了 Grid 组件,并设置了别名,这样咱们能够用 xtype 的方式调用这个组件,另外咱们也添加了 store 和 columns 的配置。 接下来咱们须要添加这个视图到 Students控制器。由于咱们用 'widget.studentlist' 设置了别名,因此咱们可使用 studentlist 做为xtype,就像咱们使用以前使用的 'panel'
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', views: [ 'student.List'//添加view视图 ], init: ... onPanelRendered: ... });
接下来修改 app.js 让视图在viewport中渲染,须要修改 launch 方法
Ext.application({ ... launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: { xtype: 'studentlist' } }); } });
惟一须要注意的是咱们在views数组中指定了 'student.List' ,这告诉应用去自动加载对应的文件,ExtJS4 的动态加载系统会根据规则从服务器自动拉取文件,例如student.List就是规则,把.替换成/就是文件存放路径。刷新一下页面便可看到效果
分三步完成对对编辑窗体的控制
注意 onPanelRendered 方法依然被调用,由于咱们的grid依然知足 'viewport > panel' 选择器,由于咱们的视图继承自 Grid ,从而继承自 Panel。如今咱们须要收紧一下选择器,咱们使用xtype做为选择器替换以前的 'viewport > panel' ,监听双击事件,以便继续作编辑用户信息:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', views: [ 'student.List' ], init: function() { this.control({ 'studentlist': { itemdblclick: this. editStudent//添加行双击事件 } }); }, editStudent: function(grid, record) { console.log('Double clicked on ' + record.get('name')); } });
注意咱们更换了组件查询选择器为 'studentlist' ,监听的事件更改成 'itemdblclick' ,响应函数设置为 editStudent,如今只是简单的日志出双击行的name属性。
能够看到日志是正确的,但咱们实际想作的是编辑用户信息,让咱们如今作,建立一个新的视图 app/view/student/Edit.js
Ext.define('FWY.view.student.Edit', { extend: 'Ext.window.Window', alias : 'widget.studentedit', title : '修改学生信息', layout: 'fit', autoShow: true, initComponent: function() { this.items = [ { xtype: 'form', items: [ { xtype: 'textfield', name : 'name', fieldLabel: '姓名' }, { xtype: 'textfield', name : 'age', fieldLabel: '年龄' }, { xtype: 'textfield', name : 'sex', fieldLabel: '性别' } ] } ]; this.buttons = [ { text: '保存', action: 'save' }, { text: '取消', scope: this, handler: this.close } ]; this.callParent(arguments); } });
接下来咱们要作的就是在控制器加载这个视图,渲染而且加载用户信息:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', views: [ 'student.List', 'student.Edit'//添加edit视图 ], init: ... editStudent: function(grid, record) { var view = Ext.widget('studentedit');//注册组件,显示窗口 view.down('form').loadRecord(record);//加载数据到表单中 } });
首先咱们用 Ext.widget 方法建立了视图,这个方法等同于 Ext.create('widget.studentedit')
,而后咱们又一次借助组件查询找到了窗口中的表单,每一个ExtJS4中的组件都有一个 down
方法,能够借助组件查询支持的选择器来迅速找到任意下层的组件,双击表格中的一行能够看到弹窗效果。
如今咱们有了表单,能够开始编辑和保存用户信息了,可是这以前须要作一点点重构。 FWY.view.student.List 建立了一个内联的 Store ,这样能够工做可是咱们须要把 Store 分离出来以便咱们在应用的其余位置能够引用并更新其中的信息,咱们把它放在它应该在的文件中 app/store/Students.js
:
Ext.define('FWY.store.Students', { extend: 'Ext.data.Store', fields: ['id','name', 'age','sex'], data: [ {id:1,name: '张三', age: 30,sex:'男'}, {id:2,name: '李四', age: 20,sex:'女'} ] });
如今咱们须要作两处变动,首先咱们须要让 Students 初始化的时候加载这个 Store :
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', stores: ['Students'],//加载store ... });
而后咱们要把以前直接在视图中内联的store更改掉,
Ext.define('FWY.view.student.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.studentlist', store: 'Students',//引用Store ... });
控制器的代码中中引入了store,store会被自动加载到页面并赋予一个storeId,这让视图中使用store变的容易(这个例子中,只要配置 store: 'Students' 就能够了) 如今咱们只是在store中内联的定义了四个字段 (id,name,age,sex),这样能够工做了。
进一步重构:
ExtJS4中有一个强大的 Ext.data.Model类,在编辑用户的时候咱们能够借助它,使用Model重构下Store,在 app/model/Student.js
中建立一个Model:
Ext.define('FWY.model.Student', { extend: 'Ext.data.Model', fields: ['id','name','age','sex'] });
这就是定义咱们的Model须要作的,如今须要让Store引用Model替换掉使用内联字段的方式,而且让控制器也引用Model:
//修改控制器,引用Model Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', stores: ['Students'], models: ['Student'], ... }); //修改store,引用Model Ext.define('FWY.store.Students', { extend: 'Ext.data.Store', model: 'FWY.model.Student', data: [ {id:1,name: '张三1', age: 30,sex:'男'}, {id:2,name: '李四1', age: 21,sex:'女'} ] });
如今咱们有了一个用户数据表,双击每⼀一行都能打开一个编辑窗口,如今要作的是保存编辑变动,编辑窗口有一个编辑表单,还有保存按钮,如今咱们更新一下控制器让保存按钮有响应:
Ext.define('FWY.controller.Students', { init: function() { this.control({ 'viewport > studentlist': { itemdblclick: this.editStudent }, 'studentedit button[action=save]': {//获取studentedit视图中的button配置action=‘save’的按钮事件 click: this.updateStudent } }); }, updateStudent: function(button) { console.log('clicked the Save button'); } });
接下来填充 updateStudent 真正的逻辑。咱们须要把数据从表单中取出,再 设置回store中:
updateStudent: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); }
让咱们增长和服务器端的交互完成这个例子。如今咱们仍是应编码了两行表格的数 据,如今让咱们经过ajax加载:
Ext.define('FWY.store.Students', { extend: 'Ext.data.Store', model: 'FWY.model.Student', autoLoad: true, proxy: { type: 'ajax', url: 'data/students.json', reader: { type: 'json', root: 'students', successProperty: 'success' } } });
这里咱们去除了 'data' 属性,替换成 proxy ,代理是让Store或者Model加载和保存数据的一个方式,有AJAX,JSONP,HTML5的localStorage本地存储等。这里咱们使用了一个简单的AJAX代理,让它经过URL 'data/students.json' 加载数据。
咱们同时给代理附加了一个reader,reader是用来把服务器返回的数据解码成Store能理解的格式,此次咱们使用了JSON reader,而且指定了root和 successProperty 配置(JSON reader的详细配置看文档),最后咱们建立一下数据文件 data/students.json
,输入内容:
{ success: true, users: [ {id: 1, name: 'zhang', email: 'zhang@126.com'}, {id: 2, name: 'lishi', email: 'lishi@126.com'} ] }
其余的变动就是咱们给Store设置了 autoLoad 属性并设置为 true ,这意味着Store生成以后会自动让Proxy加载数据,刷新⼀一下页面应该是看到和以前一样的结果,不一样的是如今不是在程序中存在硬编码数据了,最后的事情是将变动传回服务器端,这个例子中咱们使用静态的JSON文件,没有使用数据库,但足够说明咱们例子的了,首先作一点点变化告知proxy用于更新的url:
proxy: { type: 'ajax', api: { read: 'data/students.json', update: 'data/updateStudents.json', }, reader: { type: 'json', root: 'students', successProperty: 'success' } }
依然从 students.json 读取数据,可是变动会发送到 updateStudents.json ,这里咱们作⼀个模拟的应答回包以让咱们知道程序能够正确工做, updateStudents.json 只须要包含{"success":true}
,其余要作的就是让Store在编辑以后进行同步,须要在 updateStudent 函数中增长一行代码:
updateStudent: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); this.getStudentsStore().sync();//将数据同步到store中 }
最后附上本篇的代码:点击下载
--本篇完--