Ext JS提供了mvc和mvvm的应用程序框架支持,这两种架构方法都是关注于将应用程序代码和业务逻辑分离。每一种方法都有本身的优势,这取决于怎么分离应用程序模块。下面咱们就简单看一下:
什么是MVC框架?
在一个MVC框架中,大多数的类要么是模型(model)要么是视图(view)要么是控制器(controller)。用户(user)与视图(view)交互,视图(view)呢又显示模型(model)中的数据(data)。这些交互都被控制器(controller)监控,控制器(controller)又在须要的时候经过更新模型(model)和视图(view)与响应交互。
什么是mvvm框架?
mvvm简单说来就是在MVC的基础上,新增了一个相似数据仓库的概念。就是说我有了数据模型(model),也有了视图展示(view),还有对其分发处理的控制器(controller)以后,多了一个储蓄所,以下图:

上面的三个模块就是传统的MVC模式,ExtJS在此基础上提供了VM模块。它发出请求,经过咱们指定的模型来组装后返回,以后每次有数据请求时,均可以从这里获取数据,减小了服务器的请求次数。
层级结构关系
下面是ExtJS的MVVM框架的目录结构,对于不知道Extjs环境如何部署的朋友,可参考这篇文章《
Extjs5.0集成Eclipse
》。

这里可以很清楚的看到controller、model、store及view三个层次,也就是MVVM的分层目录。这种思想在ExtJS4.X中也有体现,可是在5.X以后添加了新的特性,就是viewmodel和viewcontroller:


这里的viewmodel就是MVVM中的VM,viewcontroller就是在原来controller基础上的演进,目的是实现了动态加载,防止一次性加载过多的js致使的数据阻塞的问题。

上图就展示了login文件夹下三个js文件的关系。Login.js是一个登录窗体,调用的方法逻辑都在LoginController.js中,项目初始化的参数存储在LoginModel.js,各司其职,很是明了。
下篇就以一个登陆的实例来介绍下ExtJS5.0中是如何实现的。
一、校验
对于最基本的校验固然是不能缺乏的。这里的校验包括对空值、输入字段的长度及是否正确都进行了校验,
好比以下两图:
二、验证码刷新
如上图中验证码为
R8DR,点击后变为
0PSS。
在后台对验证码进行了大小写处理,因为大些更容易辨认,因此在前台生成时用大些,输入的验证码大小写都是能够的。
三、重置与肯定
点击重置后会将输入的内容清空
点击肯定后则进入后台页面(目前仍是比较丑陋的),这里利用session进行了限制,若是正常登录后刷新页面是不会从新登录的。
下一篇螃蟹就登录的ExtJS代码进行详解,核心代码以下:
- Ext.define('app.view.login.Login', {
- extend: 'Ext.window.Window',
-
- requires: [
- 'app.view.login.LoginController',
- 'app.view.login.LoginModel',
- 'Ext.form.Panel',
- 'Ext.button.Button',
- 'Ext.form.field.Text',
- 'Ext.form.field.ComboBox'
- ],
-
- viewModel: 'login',
-
- controller: 'login',
- title: 'IT学习者-人事管理系统登录',
- closable: false,
- width : 400,
- height : 230,
- cls: 'login',
- buttonAlign : 'center',
-
- items:[{
- xtype : "displayfield",
- value : "",
- height:30,
- margin : "0 0 0 0"
- },{
- layout : "column",
- items : [{
- columnWidth:.7,
- xtype: 'form',
- reference: 'form',
- defaults : {
- labelSeparator : ':',
- labelWidth : 60,
- width : 200,
- labelAlign : 'left'
- },
- defaultType : 'textfield',
- items : [ {
- xtype : 'textfield',
- fieldLabel : ' 用户名 ',
- emptyText:"请输入用户名",
- regex : /([A-Za-z]{1})\w{1,19}/,
- regexText : '用户名格式有误',
- name : 'loginName',
- allowBlank : false,
- blankText : '用户名不能为空',
- minLength : 5,
- minLengthText : '用户名的长度为[5-16]',
- maxLength : 16,
- maxLengthText : '用户名的长度为[5-16]',
- margin : "10 10 10 50"
- }, {
- xtype : 'textfield',
- name : 'password',
- inputType : 'password',
- fieldLabel : '密 码',
- fieldCls : 'password',
- emptyText:"请输入密码",
- inputType : 'password',
- allowBlank:false,
- blankText : '密码不能为空',
- minLength : 5,
- minLengthText : '密码的长度为[5-20]',
- maxLength : 20,
- maxLengthText : '密码的长度为[5-20]',
- margin : "15 10 10 50"
- },{
- xtype:'textfield',
- width:120,
- fieldLabel : '验证码',
- name : 'authcode',
- allowBlank:false,
- blankText : '验证码不能为空',
- margin : "15 0 0 50"
- },{
- xtype:'panel',
- columnWidth:.4,
- height:30,
- html:"<a href='#' onclick='javascript:refreshCode();'><img id='validateCodeImg' title='点击更换' alt='点击更换' src='authCode' /></a>",
- margin : "-26 0 175"
- }]
- },{
-
- layout:'fit',
- bodyStyle: 'background:transparent',//设置为透明,不不妨碍更换主题了
- columnWidth:.28,
- height:120,
- items:[{
- xtype : "displayfield",
- hideLabel : true,
- margin : "-105 0 0 0",
- value : "<img src='./images/itxxz.png' />"
- }]
- }]
- }],
- buttons: [{
- text: '肯定',
- listeners: {
- click: 'onLoginClick'
- }
- },{
- text: "重置",
- handler: function () {
- this.up('window').down('form').getForm().reset();
- }
- }]
- });
- //刷新验证码
- function refreshCode() {
- document.getElementById("validateCodeImg").src = "authCode?"+Math.random();
- }
的 javascript
刚开始学习的时候,螃蟹也是一头雾水,可是因为ExtJS5.0的书写及其规范,API至关完整,因而,在学习的时候螃蟹尽可能的不脱离其设计思想,在原有的框架基础上进行开发改进。
好比咱们定义一个类,就能够经过如下的语法进行定义:
html
- Ext.define('TextClass', {
- name: 'itxxz',
- value: 'IT学习者'
- });
这样,咱们就定义了一个TextClass的类,这个类里有name和value两个属性,之后使用的时候,直接new或者Create就能够了。
就比如上篇中咱们定义了登录窗口,
java
- Ext.define('app.view.login.Login', {..}
这里的定义就与TextClass的定义不一样了,学过java的朋友可能更好理解一些,这里的 app.view.login.Login 其实就是一个文件的路径,而app并不是以下图的根路径,而是咱们定义的命名空间,具体的定义规则在《Extjs5.0集成Eclipse》中的sencha命令已有介绍。
那么view.login.Login就是文件的路径了,好比咱们的登录窗口是定义的Login.js文件,那么定义这个类的时候,规则就是命名空间加上路径最后以类的名字结尾,这样就能够找到该文件了,就比如咱们定义的 app.view.login.Login同样,以下图:

这时候问题就来了,若是咱们想定义一个window窗口,该如何下手?
在ExtJS中也引入了集成的思想,这样就避免了咱们重复发明轮子的烦恼,想实现什么,只要extend一下就能够了:
ajax
- extend: 'Ext.window.Window',
在咱们日常写js的时候每每会有不少js的引入来相互配合,这在ExtJS中也有体现,就是经过require来实现:
数据库
- requires: [
- 'app.view.login.LoginController',
- 'app.view.login.LoginModel',
- 'Ext.form.Panel',
- 'Ext.button.Button',
- 'Ext.form.field.Text',
- 'Ext.form.field.ComboBox'
- ],
这是咱们须要的js文件,经过本篇讲解的类的定义规则,相信应该能够看懂这种命名规范了。
下面咱们来看一下特殊的写法:
json
- viewModel: 'login',
-
- controller: 'login',
这里是引用viewModel和controller,有关这方面的介绍可看一下《ExtJS5.0的mvvm分层思想》。
经过上图中能够看到,login文件夹下一共定义了三个文件:Login.js、LoginController.js、LoginModel.js 。
Login.js的代码在上一篇已经张贴了出来,下面先简单看下另外的代码:
LoginController.js
服务器
- Ext.define('app.view.login.LoginController', {
- extend: 'Ext.app.ViewController',
- alias: 'controller.login',
-
- loginText: 'Logging in...',
-
- constructor: function () {
- this.callParent(arguments);
-
- this.loginManager = new app.LoginManager({});
- },
-
- onSpecialKey: function(field, e) {
- if (e.getKey() === e.ENTER) {
- this.doLogin();
- }
- },
-
- onLoginClick: function() {
- this.doLogin();
- },
-
- doLogin: function() {
- var form = this.lookupReference('form');
- if (form.isValid()) {
- Ext.getBody().mask(this.loginText);
- this.loginManager.login({
- data: form.getValues(),
- scope: this,
- success: 'onLoginSuccess',
- failure: 'onLoginFailure'
- });
- }
- },
-
- onLoginFailure: function() {
- Ext.getBody().unmask();
- },
-
- onLoginSuccess: function() {
- Ext.getBody().unmask();
- this.fireViewEvent('login', this.getView(), null, null, this.loginManager);
- }
- });
LoginModel.js
cookie
- Ext.define('app.view.login.LoginModel', {
- extend: 'Ext.app.ViewModel',
- alias: 'viewmodel.login',
-
- // Just some data to seed the process. This might be pulled from a cookie or other
- // in a real app.
- data: {
- defaultOrg: 1,
- username: 'IT学习者-螃蟹'
- }
- });
在上面两贴代码的第三行,都有一个alias属性,它的做用就是为该类起一个别名,用过数据库的朋友对此应该不陌生。
这样再回头看这两行代码应该知文识意了,没错,就是根据别名来引用的。
session
- viewModel: 'login',
-
- controller: 'login',
在这样引用后,ExtJS会默认生成这两个文件的对象了。
这篇就先介绍类的定义继承及引用,下篇咱们继续分析 架构
在前几篇的教程中,螃蟹已经简单介绍了Extjs如何加载java后台的数据,不过是以项目中所定义的规范加载的,鉴于很多朋友依旧咨询这方面的问题,这里就以store为例,单独说明一下。
这是用于加载数据的请求,有些相似ajax,指定请求的url,请求的字段属性,也就是fields中定义的属性,再一个就是要指定根节点root。
- var store_itxxz = Ext.create('Ext.data.Store', {
- storeId:'simpsonsStore',
- fields:['empid', 'userName', 'sex'],
- proxy: {
- type: 'ajax',
- url: 'emp/queryAll',
- reader: {
- type: 'json',
- root: 'data'
- }
- },
- autoLoad: true
- });
而后就是如何调用的问题
- var grid = Ext.create('Ext.grid.Panel', {
- title: 'Simpsons',
- store: store_itxxz,
- columns: [
- {header: '登陆名', dataIndex: 'userName'},
- {header: 'empid', dataIndex: 'empid', flex:1}
- ],
- dockedItems: [{
- xtype: 'pagingtoolbar',
- store: store_itxxz, // GridPanel使用相同的数据源
- dock: 'bottom',
- displayInfo: true
- }]
- });
这里第3行和第10行的store:store_itxxz中,左边的store是指grid中定义的数据源属性,右边的store_itxxz是指咱们数据对象。
第10行是用来加载列表的数据,作显示用,第10行为分页用。
java后台代码:
- /**
- * 员工列表
- * @return
- * @throws Exception
- */
- @RequestMapping("/queryAll")
- @ResponseBody
- public Map<String,List<Employee>> queryAllEmps() throws Exception{
- log.info("员工列表");
- List<Employee> allList = employeeService.selectAllEmps();
- Map<String,List<Employee>> map = new HashMap<String,List<Employee>>();
- map.put("data", allList);
- System.out.println(map);
- return map;
- }
后台代码中返回的是一个map,以ajax形式返回,是SpringMVC的基本用法,这里就很少介绍了。
第12行,map中put了一个key为data,value为allList的元素,这里的data就是store_itxxz中的root指定的根节点。
也能够修改一下,好比根节点叫作itxxz_data,那么再此处只须要将key改成itxxz_data,store_itxxz中奖root的定义data也改成itxxz_data就能够了。
数据的返回格式以下:
{ data: [ {empid: '001', userName:'IT学习者', sex:'男'}, {empid: '002', userNmae:'螃蟹', sex:'女'} ] }
效果图可参考《Extjs5.0 GridPanel数据动态加载》