一、ExtJs 4.x MVC模式的原理与作用

大规模客户端应用通常不好实现不好组织也不好维护,因为功能和人力的不断增加,这些应用的规模很快就会超出掌控能力,ExtJS4带来了一个新的应用架构,不但可以组织代码,还可以减少实现的内容。

新的应用架构遵照一个类MVC的模式,模型(Models)和控制器(Controllers)首次被引入。业界有很多种MVC架构,基本大同小异,ExtJS4的定义如下:

a.Model模型:模型是字段和它们的数据的集合,例如User模型带有username和password字段,模型知道如何持久化自己的数据,并且可以和其他模型关联,模型跟ExtJS 3 中的Record类有点像(区别是,Record只是单纯的扁平结构,而Model可以nest),通常都用在Store中去展示grid和其他组件的数据。

b.View视图:视图是组件的一种,专注于界面展示 – grid, tree, panel 都是view。

c.Controllers控制器:一个安放所有使你的app正确工作的代码的位置,具体一点应该是所有动作,例如如何渲染view,如何初始化model,和app的其他逻辑。

请注意:MVC是一个框架,不是设计模式,更多的内容请参考: 百度百科

框架与设计模式虽然相似,但却有着根本的不同。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述,它比框架更抽象;框架可以用代码表示,也能直接执行或复用,而对模式而言只有实例才能用代码表示;设计模式是比框架更小的元素,一个框架中往往含有一个或多个设计模式,框架总是针对某一特定应用领域,但同一模式却可适用于各种应用。可以说,框架是软件,而设计模式是软件的知识。

简而言之:设计模式是大智慧,用来对软件设计进行分工;框架模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。

二、ExtJs 4 MVC框架搭建

2.1、文件结构

ExtJS 4 应用都遵循一个统一的目录结构,每个应有都相同 MVC中,所有类都放在app目录里面,这个目录可以有子目录,代表的是命名空间(一个子目录对应一个命名空间),使用不同的目录存放views,models,controllers,stores。当我们完成例子的时候,目录结构应该和下图一样:

ExtJS SDK必须的文件在目录ext4中,因此,index.html应该引入extjs必须的js和css,以及app.js文件

2.2、在app.js中创建应用

每个ExtJS 4的应用都从一个Application类的实例开始,这个实例包含应用的全局配置(例如应用的名字),这个实例也负责维护对全部模型、视图、控制器的引用的维护,还有一个launch函数,会在所有加载项加载完成之后调用。

首先需要选择一个全局命名空间,所有ExtJS4应用都需要有一个全局命名空间,以让所有应用中的类安放到其中:

  1. Ext.application({
  2. requires: ['Ext.container.Viewport'],
  3. name: 'FWY',//定义的命名空间
  4. appFolder: 'app',//指明应用的根目录
  5. launch: function() {
  6. Ext.create('Ext.container.Viewport', {
  7. layout: 'fit',
  8. items: [
  9. {
  10. xtype: 'panel',
  11. title: '标题',
  12. html : '内容'
  13. }
  14. ]
  15. });
  16. }
  17. });

2.3、定义一个控制器

控制器是应用的粘合剂,它们所作的事情就是监听事件并执行动作,继续我们的应用,创建一个控制器。创建app/controller/Students.js这个文件,并添加如下代码:

  1. Ext.define('FWY.controller.Students', {
  2. extend: 'Ext.app.Controller',
  3. init: function() {
  4. console.debug("trigger controller init event");
  5. }
  6. });

接下来在app.js中添加对Students控制器的引用:

  1. Ext.application({
  2. ...
  3. controllers: [
  4. 'Students' //对应于controller文件夹下面的Students.js
  5. ],
  6. ...
  7. });

当我们通过index.html查看应用,Students控制器会被自动加载(因为在app.js的Application中增加了引用),并且Students的init方法会在launch之前调用。

init方法是个极好的地方,可以用来设置如何和view交互,通常都使用Controller的一个方法control,control方法使得监听view的事件变的容易,更新一下控制器,让它告知我们panel何时渲染:

  1. Ext.define('FWY.controller.Students', {
  2. extend: 'Ext.app.Controller',
  3. init: function() {
  4. this.control({
  5. 'viewport > panel': {
  6. render: this.onPanelRendered
  7. }
  8. });
  9. },
  10. onPanelRendered: function() {
  11. console.debug('该panel被渲染了');
  12. }
  13. });

我们已经更新了init方法,使用this.controll给视图设置监听器。这个controll方法,使用最新的组件查询引擎(ComponentQuery)可以快速方便的找到页面上的组件。如果你对ComponentQuery不熟悉,可以查看ComponentQuery文档进行详细了解。简要一点,ComponentQuery可以允许我们使用一个类似css选择器的方式找到组件。

在例子的init方法中我们应用了'viewport > panel',可以解释为“查找Viewport直接后代中的所有Panel组件”,然后我们提供了一个对象匹配事件名称(这个例子中只用了render)来提供响应函数。全部的影响就是无论哪个组件符合我们的选择器,当它的render事件触发时,我们的onPanelRendered函数都会被调用。

三、创建ExtJs4 MVC应用

1、定义一个视图

直到现在,我们的应用只有很少代码,只有两个文 件 app.js 和 app/controller/Students.js,现在我们想增加一个grid显示所有系统中的学生列表,修改3处:

(1)、添加view/List.js视图

是时候更好的组织一下逻辑并开始使用视图了。

视图也是组件,通常都是ExtJS现有组件的子类,现在准备创建学生表,先创建 app/view/student/List.js ,添加代码:

  1. Ext.define('FWY.view.student.List' ,{
  2. extend: 'Ext.grid.Panel',
  3. alias : 'widget.studentlist',
  4. title : '学生信息列表',
  5. initComponent: function() {
  6. this.store = {
  7. fields: ['id','name', 'age','sex'],
  8. data : [
  9. {id:1,name: 'zhangsan', age: 18,sex:'boy'},
  10. {id:2,name: 'lishi', age: 20,sex:'girl'}
  11. ]};
  12. this.columns = [
  13. {header: '编号', dataIndex: 'id', flex: 1},
  14. {header: '姓名', dataIndex: 'name', flex: 1},
  15. {header: '年龄', dataIndex: 'age', flex: 1},
  16. {header: '性别', dataIndex: 'sex', flex: 1}
  17. ];
  18. this.callParent(arguments);
  19. }
  20. });
(2)、修改controller/Students.js

我们的视图类就是一个普通的类,这个例子中我们扩展了 Grid 组件,并设置了别名,这样我们可以用 xtype 的方式调用这个组件,另外我们也添加了 store 和 columns 的配置。 接下来我们需要添加这个视图到 Students控制器。因为我们用 'widget.studentlist' 设置了别名,所以我们可以使用 studentlist 作为xtype,就像我们使用之前使用的 'panel'

  1. Ext.define('FWY.controller.Students', {
  2. extend: 'Ext.app.Controller',
  3. views: [
  4. 'student.List'//添加view视图
  5. ],
  6. init: ...
  7. onPanelRendered: ...
  8. });
(3)、修改app.js,加载视图

接下来修改 app.js 让视图在viewport中渲染,需要修改 launch 方法

  1. Ext.application({
  2. ...
  3. launch: function() {
  4. Ext.create('Ext.container.Viewport', {
  5. layout: 'fit',
  6. items: {
  7. xtype: 'studentlist'
  8. }
  9. });
  10. }
  11. });

唯一需要注意的是我们在views数组中指定了 'student.List' ,这告诉应用去自动加载对应的文件,ExtJS4 的动态加载系统会根据规则从服务器自动拉取文件,例如student.List就是规则,把.替换成/就是文件存放路径。刷新一下页面即可看到效果

2、添加对列表的控制

分三步完成对对编辑窗体的控制

(1)、为grid绑定双击事件

注意 onPanelRendered 方法依然被调用,因为我们的grid依然满足 'viewport > panel' 选择器,因为我们的视图继承自 Grid ,从而继承自 Panel。现在我们需要收紧一下选择器,我们使用xtype作为选择器替换之前的 'viewport > panel' ,监听双击事件,以便继续做编辑用户信息:

  1. Ext.define('FWY.controller.Students', {
  2. extend: 'Ext.app.Controller',
  3. views: [
  4. 'student.List'
  5. ],
  6. init: function() {
  7. this.control({
  8. 'studentlist': {
  9. itemdblclick: this. editStudent//添加行双击事件
  10. }
  11. });
  12. },
  13. editStudent: function(grid, record) {
  14. console.log('Double clicked on ' + record.get('name'));
  15. }
  16. });

注意我们更换了组件查询选择器为 'studentlist' ,监听的事件更改为 'itemdblclick' ,响应函数设置为 editStudent,现在只是简单的日志出双击行的name属性。

(2)、创建view/student/Edit.js视图

可以看到日志是正确的,但我们实际想做的是编辑用户信息,让我们现在做,创建一个新的视图 app/view/student/Edit.js

  1. Ext.define('FWY.view.student.Edit', {
  2. extend: 'Ext.window.Window',
  3. alias : 'widget.studentedit',
  4. title : '修改学生信息',
  5. layout: 'fit',
  6. autoShow: true,
  7. initComponent: function() {
  8. this.items = [
  9. {
  10. xtype: 'form',
  11. items: [
  12. {
  13. xtype: 'textfield',
  14. name : 'name',
  15. fieldLabel: '姓名'
  16. },
  17. {
  18. xtype: 'textfield',
  19. name : 'age',
  20. fieldLabel: '年龄'
  21. },
  22. {
  23. xtype: 'textfield',
  24. name : 'sex',
  25. fieldLabel: '性别'
  26. }
  27. ]
  28. }
  29. ];
  30. this.buttons = [
  31. {
  32. text: '保存',
  33. action: 'save'
  34. },
  35. {
  36. text: '取消',
  37. scope: this,
  38. handler: this.close
  39. }
  40. ];
  41.  
  42. this.callParent(arguments);
  43. }
  44. });
(3)、修改控制器加载视图

接下来我们要做的就是在控制器加载这个视图,渲染并且加载用户信息:

  1. Ext.define('FWY.controller.Students', {
  2. extend: 'Ext.app.Controller',
  3. views: [
  4. 'student.List',
  5. 'student.Edit'//添加edit视图
  6. ],
  7. init: ...
  8. editStudent: function(grid, record) {
  9. var view = Ext.widget('studentedit');//注册组件,显示窗口
  10. view.down('form').loadRecord(record);//加载数据到表单中
  11. }
  12. });

首先我们用 Ext.widget 方法创建了视图,这个方法等同于 Ext.create('widget.studentedit') ,然后我们又一次借助组件查询找到了窗口中的表单,每个ExtJS4中的组件都有一个 down方法,可以借助组件查询支持的选择器来迅速找到任意下层的组件,双击表格中的一行可以看到弹窗效果。

3、创建Store和Model进行重构

现在我们有了表单,可以开始编辑和保存用户信息了,但是这之前需要做一点点重构。 FWY.view.student.List 创建了一个内联的 Store ,这样可以工作但是我们需要把 Store 分离出来以便我们在应用的其他位置可以引用并更新其中的信息,我们把它放在它应该在的文件中 app/store/Students.js

  1. Ext.define('FWY.store.Students', {
  2. extend: 'Ext.data.Store',
  3. fields: ['id','name', 'age','sex'],
  4. data: [
  5. {id:1,name: '张三', age: 30,sex:'男'},
  6. {id:2,name: '李四', age: 20,sex:'女'}
  7. ]
  8. });

现在我们需要做两处变更,首先我们需要让 Students 初始化的时候加载这个 Store :

  1. Ext.define('FWY.controller.Students', {
  2. extend: 'Ext.app.Controller',
  3. stores: ['Students'],//加载store
  4. ...
  5. });

然后我们要把之前直接在视图中内联的store更改掉,

  1. Ext.define('FWY.view.student.List' ,{
  2. extend: 'Ext.grid.Panel',
  3. alias : 'widget.studentlist',
  4. store: 'Students',//引用Store
  5. ...
  6. });

控制器的代码中中引入了store,store会被自动加载到页面并赋予一个storeId,这让视图中使用store变的容易(这个例子中,只要配置 store: 'Students' 就可以了) 现在我们只是在store中内联的定义了四个字段 (id,name,age,sex),这样可以工作了。

进一步重构:

ExtJS4中有一个强大的 Ext.data.Model类,在编辑用户的时候我们可以借助它,使用Model重构下Store,在 app/model/Student.js中创建一个Model:

  1. Ext.define('FWY.model.Student', {
  2. extend: 'Ext.data.Model',
  3. fields: ['id','name','age','sex']
  4. });

这就是定义我们的Model需要做的,现在需要让Store引用Model替换掉使用内联字段的方式,并且让控制器也引用Model:

  1. //修改控制器,引用Model
  2. Ext.define('FWY.controller.Students', {
  3. extend: 'Ext.app.Controller',
  4. stores: ['Students'],
  5. models: ['Student'],
  6. ...
  7. });
  8. //修改store,引用Model
  9. Ext.define('FWY.store.Students', {
  10. extend: 'Ext.data.Store',
  11. model: 'FWY.model.Student',
  12. data: [
  13. {id:1,name: '张三1', age: 30,sex:'男'},
  14. {id:2,name: '李四1', age: 21,sex:'女'}
  15. ]
  16. });

4、利用模型保存数据

现在我们有了一个用户数据表,双击每⼀一行都能打开一个编辑窗口,现在要做的是保存编辑变更,编辑窗口有一个编辑表单,还有保存按钮,现在我们更新一下控制器让保存按钮有响应:

  1. Ext.define('FWY.controller.Students', {
  2. init: function() {
  3. this.control({
  4. 'viewport > studentlist': {
  5. itemdblclick: this.editStudent
  6. },
  7. 'studentedit button[action=save]': {//获取studentedit视图中的button配置action=‘save’的按钮事件
  8. click: this.updateStudent
  9. }
  10. });
  11. },
  12. updateStudent: function(button) {
  13. console.log('clicked the Save button');
  14. }
  15. });

接下来填充 updateStudent 真正的逻辑。我们需要把数据从表单中取出,再 设置回store中:

  1. updateStudent: function(button) {
  2. var win = button.up('window'),
  3. form = win.down('form'),
  4. record = form.getRecord(),
  5. values = form.getValues();
  6. record.set(values);
  7. win.close();
  8. }

5、保存到服务器

让我们增加和服务器端的交互完成这个例子。现在我们还是应编码了两行表格的数 据,现在让我们通过ajax加载:

  1. Ext.define('FWY.store.Students', {
  2. extend: 'Ext.data.Store',
  3. model: 'FWY.model.Student',
  4. autoLoad: true,
  5. proxy: {
  6. type: 'ajax',
  7. url: 'data/students.json',
  8. reader: {
  9. type: 'json',
  10. root: 'students',
  11. successProperty: 'success'
  12. }
  13. }
  14. });

这里我们去除了 '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 ,输入内容:

  1. {
  2. success: true,
  3. users: [
  4. {id: 1, name: 'zhang', email: 'zhang@126.com'},
  5. {id: 2, name: 'lishi', email: 'lishi@126.com'}
  6.   ]
  7. }

其他的变更就是我们给Store设置了 autoLoad 属性并设置为 true ,这意味着Store生成之后会自动让Proxy加载数据,刷新⼀一下页面应该是看到和之前同样的结果,不同的是现在不是在程序中存在硬编码数据了,最后的事情是将变更传回服务器端,这个例子中我们使用静态的JSON文件,没有使用数据库,但足够说明我们例子的了,首先做一点点变化告知proxy用于更新的url:

  1. proxy: {
  2. type: 'ajax',
  3. api: {
  4. read: 'data/students.json',
  5. update: 'data/updateStudents.json',
  6. },
  7. reader: {
  8. type: 'json',
  9. root: 'students',
  10. successProperty: 'success'
  11. }
  12. }

依然从 students.json 读取数据,但是变更会发送到 updateStudents.json ,这里我们做⼀个模拟的应答回包以让我们知道程序可以正确工作, updateStudents.json 只需要包含{"success":true},其他要做的就是让Store在编辑之后进行同步,需要在 updateStudent 函数中增加一行代码:

  1. updateStudent: function(button) {
  2. var win = button.up('window'),
  3. form = win.down('form'),
  4. record = form.getRecord(),
  5. values = form.getValues();
  6. record.set(values);
  7. win.close();
  8. this.getStudentsStore().sync();//将数据同步到store中
  9. }

最后附上本篇的代码:点击下载

--本篇完--

ExtJs 4 中的MVC应用架构的更多相关文章

  1. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  2. 二十七、EFW框架BS系统开发中的MVC模式探讨

    回<[开源]EFW框架系列文章索引>        EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://p ...

  3. MVC实用架构设计(三)——EF-Code First(5):二级缓存

    前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...

  4. MVC实用架构设计(三)——EF-Code First(4):数据查询

    前言 首先对大家表示抱歉,这个系列已经将近一个月没有更新了,相信大家等本篇更新都等得快失望了.实在没办法,由于本人水平有限,写篇博客基本上要大半天的时间,最近实在是抽不出这么长段的空闲时间来写.另外也 ...

  5. MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    前言 经过前面EF的<第一篇>与<第二篇>,我们的数据层功能已经较为完善了,但有不少代码相似度较高,比如负责实体映射的 EntityConfiguration,负责仓储操作的I ...

  6. Mvc项目架构分享之项目扩展

    Mvc项目架构分享之项目扩展 Contents 系列一[架构概览] 0.项目简介 1.项目解决方案分层方案 2.所用到的技术 3.项目引用关系 系列二[架构搭建初步] 4.项目架构各部分解析 5.项目 ...

  7. Asp.net mvc项目架构分享系列之架构概览

    Asp.net mvc项目架构分享系列之架构概览 Contents 系列一[架构概览] 0.项目简介 1.项目解决方案分层方案 2.所用到的技术 3.项目引用关系 系列二[架构搭建初步] 4.项目架构 ...

  8. [Android开发]- MVC的架构实现登录模块-1

    本系列博客主要展示一下,在C-S(Client - Server)系统开发当中,如何使用MVC的架构来实现安卓端的一个登录验证的模块.如果你能有基本的数据库开发,WEB开发,和安卓开发的知识,那么理解 ...

  9. PHP中的MVC

    在PHP中使用MVC越来越流行了,特别是在一些开源的框架当中.MVC足以应对大多数的情况,但还有一些情况是其不太适合的,如比较简单的个人博客,对于只有几百篇文章量级的博客,使用MVC让人觉得有些太复杂 ...

随机推荐

  1. CNN & RNN 及一些常识知识(不断扩充中)

    参考: http://blog.csdn.net/iamrichardwhite/article/details/51089199 一.神经网络的发展历史 五六十年代,提出感知机 八十年代,提出多层感 ...

  2. Excel应该这么玩——4、命名区域:搞定下拉框

    前三篇都是讲的给Excel元素命名,本篇再介绍一种命名的使用方式:命名区域.区域是多个单元格的集合,可以是单行.单列或者类似表格的单元格矩阵,也可以是不连续的多个单元格,但很少用到.当然,一个单元格也 ...

  3. sqlyong64位破解

    姓名(Name):cr173 序列号(Code):8d8120df-a5c3-4989-8f47-5afc79c56e7c 或者(OR) 姓名(Name):cr173 序列号(Code):59adfd ...

  4. jQuery表单验证插件——jquery.validate.js

    官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation 一.导入js库 <script src="../j ...

  5. AC自动机——Uva 11468 子串

    题目链接:http://vjudge.net/contest/142513#problem/A 题意:给出一些字符和各自对应的选择概率,随机选择L次后将得到一个长度为L的随机字符串S.给出K个模版串, ...

  6. c++实现螺旋矩阵分析总结

    螺旋矩阵,是这么一个东西: 1   2   3 8   9   4 7   6   5 这是一个,n*n的矩阵,由外向里一次递增,一环一环,就好像一个螺旋一样.不难想象,如果n=5,那么应该是这样的: ...

  7. jQuery 效果 —— 隐藏和显示

    jQuery 效果 -- 隐藏和显示 1.隐藏和显示 (1)在jQuery中我们可以使用hide()和show()分别隐藏和显示HTML元素: //隐藏元素 $("button") ...

  8. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的连接恢复和命令拦截

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第四篇:MVC程序中实体框架的连接恢复和 ...

  9. [HIHO1052]基因工程(找规律)

    题目链接:http://hihocoder.com/problemset/problem/1052 题意:中文题面,就是修改其中几个字符,使得[0,k-1]和[n-k,n-1]的字符相同. 会发现一个 ...

  10. HMI开发与控件

    =>控件是什么概念? 百度曰,控件是对数据和方法的封装.控件可以有自己的属性和方法.属性是控件数据的简单访问者. 对于HMI开发来说,使用控件可以快速获取到用户的交互(包括按下.释放.点击.拖动 ...