一、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应用都需要有一个全局命名空间,以让所有应用中的类安放到其中:

Ext.application({
requires: ['Ext.container.Viewport'],
name: 'FWY',//定义的命名空间
appFolder: 'app',//指明应用的根目录
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [
{
xtype: 'panel',
title: '标题',
html : '内容'
}
]
});
}
});

2.3、定义一个控制器

控制器是应用的粘合剂,它们所作的事情就是监听事件并执行动作,继续我们的应用,创建一个控制器。创建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函数都会被调用。

三、创建ExtJs4 MVC应用

1、定义一个视图

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

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

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

视图也是组件,通常都是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);
}
});
(2)、修改controller/Students.js

我们的视图类就是一个普通的类,这个例子中我们扩展了 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: ...
});
(3)、修改app.js,加载视图

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

Ext.application({
...
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: {
xtype: 'studentlist'
}
});
}
});

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

2、添加对列表的控制

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

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

注意 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属性。

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

可以看到日志是正确的,但我们实际想做的是编辑用户信息,让我们现在做,创建一个新的视图 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);
}
});
(3)、修改控制器加载视图

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

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方法,可以借助组件查询支持的选择器来迅速找到任意下层的组件,双击表格中的一行可以看到弹窗效果。

3、创建Store和Model进行重构

现在我们有了表单,可以开始编辑和保存用户信息了,但是这之前需要做一点点重构。 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:'女'}
]
});

4、利用模型保存数据

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

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();
}

5、保存到服务器

让我们增加和服务器端的交互完成这个例子。现在我们还是应编码了两行表格的数 据,现在让我们通过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中
}

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

--本篇完--

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. C++嵌套多个命名空间举例

    首先在结构上是能经得起推敲的,举个例子: test.h #pragma region 嵌套多个命名空间举例     namespace Group     {          namespace C ...

  2. 十、Java基础---------面向对象之抽象类与接口

    抽象类(abstract)     当编写一个类时,时常会为该类定义一些方法,这些方法的使用用以描述该类的行为方式,那么这些方法都有具体的方法体.但是在某些情况下,某个父类只是知道子类应该包含怎样的方 ...

  3. [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  4. StringBuilder和Append的一个程序及一个基础概念

    废话少说直接来说:比如在串口数据操作中,我们只想显示串口接收的字符串,好吧你用string[]吧,有多少个字符串(顺便说下二进制在C#中是以字符串形式出现的)就要分配多少个储存空间,自己试下,要你你干 ...

  5. python: HTML之 鼠标放上去下拉项字体显示不同颜色

    鼠标放上去下拉项字体显示不同颜色 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "h ...

  6. 页面缩放对css的影响

    昨天发现一个上线的项目css样式明显不对,但是查看别人的电脑上的页面样式都是没问题的,于是找了半天原因,原来是我的浏览器对这个页面缩放了,导致样式问题. 发现了页面缩放会作用在同一个域名下的所有页面, ...

  7. SharePoint 2013 一些小技巧

    一.添加“SharePoint 2013 切换用户”标签 相比SharePoint 2010,SharePoint2013版本去掉了切换用户登陆的功能(如下图),其实这个可以通过改welcome.as ...

  8. winform的常用公共控件和常用属性

    我们在学位winform的控件和属性的时候要学会赋值取值还有改值 公共控件: 1,Button(按钮): Enabled :是否可用 Visible:是否可见 2,CheckBox(多选项) Chec ...

  9. Giving Data Backup Option in Oracle Forms 6i

    Suppose you want to give the data backup option in Oracle Forms application to some client users, wh ...

  10. 一个java的DES加密解密类转换成C#

    一个java的des加密解密代码如下: //package com.visionsky.util; import java.security.*; //import java.util.regex.P ...