`
lxwt909
  • 浏览: 566320 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ExtJS5学习之MVVC

阅读更多

      MVVC模式并不是ExtJS首先提出的,其实ExtJS也是模仿微软的WPF中应用的MVVC设计模式。ExtJS在4.0时引入了MVC模式,在5.0时代又引入了MVVC模式。MVC模式对于大家来说应该不陌生了,MVVC是什么?要理解MVVC还是必须先了解MVC是什么?先来张MVC的结构图感受下:



 MVC是一种用来更好组织架构软件的设计模式,它把应用程序划分为3部分,各部分各司其责。

    Model:是用来表示应用程序中需要用到的数据,当然Model层中也可以包含业务逻辑和数据验证规则,以及其他各种功能接口。

   View:是用来展示数据给最终用户的视图,不同的视图可能会以不同的方式展示相同的数据给用户,比如   图表和表格都可以用来展示数据。

   Controller:它是应用程序的控制中心,它监听着应用程序中的各种事件,代理处理Model和View之间的各种命令。比如Model中数据改变了,需要通过Controller来改变View。

   那什么是MVVC呢?MVVC其实是基于MVC设计模式的一种延伸,它与MVC最关键的不同点在于它引入了ViewModel概念,ViewModel提供了Model和View之间的数据绑定,至于数据(Model)更新到视图(View)则是通过ViewController来完成。来张图感受下



 文字太抽象,还是来点代码更形象,有助于理解。

打开Eclipse,新建一个Web project,准备采用最新的MVVC设计模式来编写一个ExtJS5的Grid和Form的数据双向绑定的demo



 
 

 如图导入ExtJS5


 如果主题皮肤文件不知道怎么导入的话,请查看我的第一篇博文《ExtJS5学习之Hello World篇》
开发环境搭好了,开始编写测试代码



 请如图搭好项目结构
首先需要定义一个Application类,当然也可以不用定义,直接Ext.application({name: "appName"});这样写其实就是让ExtJS默认帮我们new一个Application实例,这里说的定义一个Application类其实就是继承ExtJS的Ext.app.Application类,进行一些我们自定义配置来覆盖默认配置,默认配置其实也是可以在Ext.application()创建application实例的运行时去覆盖的,但为了迎合面向对象的开发思想,还是定义一个Application类装装逼吧,显得高大上点。

Application.js

 

Ext.define('MyApp.Application', {
    extend: 'Ext.app.Application',
    autoCreateViewport: true,
    enableQuickTips: true,
    
    launch: function () {
    	
    }
});

Ext.define()定义一个类,类似于Java里的public class XXXX, 
extend:继承,你懂的

 

autoCreateViewport即自动帮我们创建ViewPort画布,autoCreateViewport的详细用法,我第一篇博客有详细说明,这里就不累赘了。

enableQuickTips: true 表示启用气泡提示,比如表单验证时,在文本框旁边用气泡方式显示提示信息。

在ExtJS3.x时代,只能这样写Ext.QuickTips.init();来启用,当然ExtJS5.x还是两种写法都兼容的,怎么写看各自喜好,建议采用新的写法,以免在API升级过程中旧的用法会被抛弃。

autoCreateViewport自动创建Viewport,所以我们得定义一个Viewport,

app/view/Viewport.js

 

/*********************全局视图容器类************************/
Ext.define("MyApp.view.Viewport",{
    extend:'Ext.container.Viewport',
    requires:['Ext.container.Viewport','MyApp.view.MainPanel'],
    alias : 'widget.baseviewport',
    alternateClassName: ["MyApp.Viewport"],
    layout: 'fit',
    loadMask:{msg : '正在加载,请稍候...'},
    items: [
    	{
    		xtype: 'mainpanel'
    	}
    ]
});

 viewport容器里就放了一个mainpanel,mainpanel是别名,下面继续定义一个MainPanel类

 

app/view/MainPanel.js

 

Ext.define("MyApp.view.MainPanel", {
	extend:'Ext.panel.Panel',
    alias : 'widget.mainpanel',
    alternateClassName: ["MyApp.MainPanel"],
    requires: [  
        "Ext.layout.container.Fit",
        "MyApp.controller.PersonController",
        "MyApp.viewmodel.PersonViewModel",
        "MyApp.view.PersonGridPanel",
        "MyApp.view.PersonFormPanel"
    ],
    layout: 'hbox',
    border: 0,
    defaults: {
    	flex: 1
    },
    controller: "personController",
    viewModel: {
    	type: "personViewModel"
    },
    initComponent: function () {
        var me = this;
        me.items = [
            {
            	xtype: "personGridPanel"
            },
            {
            	xtype: "personFormPanel"
            }
        ];
        me.callParent(arguments);
    }
});

 MainPanel里采用hbox水平布局,即从左到右这样水平摆放,里面放了两个子组件,personGridPanel和personFormPanel,即左边一个Grid表格右边一个FormPanel表单。

 

requires即导入当前类依赖的其他类,跟Java里的import导包差不多的意思。

controller:这个配置是5.x的MVVC模式里新引入的,其实就是MVC模式里的Controller,只不过这里的Controller的父类不再是Ext.app.Controller,变成了Ext.app.ViewController,
viewModel即当前视图的viewModel实例是什么,viewModel的参数值可以是viewMode的别名字符串,也可以是ViewModel带完整命名空间的类路径的字符串形式,也可以是viewModel的配置实例对象,比如我代码里写的那样,personViewModel是ViewModel类的别名。

app/view/PersonGridPanel.js

 

Ext.define("MyApp.view.PersonGridPanel",{
    extend:'Ext.grid.Panel',
    requires:[
        "Ext.grid.plugin.CellEditing",
        "MyApp.controller.PersonController",
        "MyApp.viewmodel.PersonViewModel",
        "MyApp.store.PersonStore"
    ],
    alias : 'widget.personGridPanel',
    alternateClassName: ["MyApp.personGridPanel"],
    uses: [
        "Ext.form.field.Text",
        "Ext.form.field.Number"
    ],
    plugins: [
        {
        	ptype: "cellediting",
        	clickToEdit: 2,
        	pluginId: "cellediting"
        }        
    ],
    publishes: ["currentPerson"],
    bind : {
    	currentPerson: "{currentPerson}",
    	store: "{personStore}",
    	title: "<strong>{currentPerson.personName}</strong>"
	},
	config: {
		currentPerson: null
	},
    controller: "personController",
    viewModel: {
    	type: "personViewModel"
    },
	listeners: {
		scope: "this",
		select: "onPersonSelect"
	},
	
	/**表格标题行**/
	header: {
	    title: "Person Grid",
	    padding: "4 9 5 9",
	    items: [
	        {
	        	text: "添加",
	        	xtype: "button",
	        	itemId: "add",
	        	handler: "onGridButtonClick"
	        },
	        {
	        	text: "撤消",
	        	xtype: "button",
	        	itemId: "reject",
	        	handler: "onGridButtonClick",
	        	tooltip: "撤消重填",
	        	disabled: true,
	        	margin: "0 0 0 15",
	        	bind: {
	        		disabled: "{!storeDirty}"
	        	}
	        },
	        {
	        	text: "提交",
	        	xtype: "button",
	        	itemId: "commit",
	        	handler: "onGridButtonClick",
	        	tooltip: "提交",
	        	disabled: true,
	        	margin: "0 0 0 15",
	        	bind: {
	        		disabled: "{!storeDirty}"
	        	}
	        }
	    ]
	},
	/**表格列头*/
	columns:[
	    {
	    	text: "姓名",
	    	width: "50%",
	    	dataIndex: "personName",
	    	editor: {
	    		xtype: "textfield",
	    		bind: "{currentPerson.personName}"
	    	}
	    },
	    {
	    	text: "年龄",
	    	width: 340,
	    	dataIndex: "age",
	    	editor: {
	    		xtype: "textfield",
	    		bind: "{currentPerson.age}"
	    	}
	    }
	],
	
	
	onPersonSelect: function(grid,person) {
		this.setCurrentPerson(person);
		var formPanel = Ext.ComponentQuery.query('personFormPanel')[0];
		formPanel.setCurrentPerson(person);
		
	},
	updateCurrentPerson: function(current,previous) {
		var sm = this.getSelectionModel();
		if(current) {
			sm.select(current);
		}
		if(previous) {
			sm.deselect(previous);
		}
	},
});

 personGridPanel里代码关键点就几处,我一一说明

 

bind:即数据绑定,把Model数据绑以key-value形式暴露出去,view视图里可以采用{key}

这种表达式来引用Model里的数据。

config:就是把在这里定义的属性自动生成get/set函数,也就是说如果你类里面需要生成get/set函数的属性可以放到config里定义,extjs会自动帮你生成get/set,这个特性在ExtJS4.x时代就有了。

controller:即当前视图的controller是谁,同理这里可以配置成controller类的别名也可以是controller类包含完整命令空间的类路径字符串。不过要记住的是,在MVVC模式里,controller都指的是Ext.app.ViewController,不再是Ext.app.Controller.

ViewModel:即MVVC中的第二个V,ExtJS5.x里的数据双向绑定就是依赖ViewModel,
app/viewmodel/PersonViewModel.js

Ext.define("MyApp.viewmodel.PersonViewModel", {
    extend : "Ext.app.ViewModel",
    alias: "viewmodel.personViewModel",
    requires:[
        "MyApp.store.PersonStore",
        "MyApp.model.PersonModel"
    ],
    data: {
    	currentPerson: null
    },
    formulas: {
        dirty: {
            bind: {
                bindTo: "{currentPerson}",
                deep: true
            },
            get: function(data) {
            	console.log(data);
                return data ? data.dirty : false;
            }
        },
        storeDirty: {
            bind: {
                bindTo: "{currentPerson}",
                deep: true
            },
            get: function() {
                return this.getStore("personStore").isDirty();
            }
        }
    },
    stores: {
        personStore: {
        	type: "personStore"
        }
    }
}); 

 viewModel的关键点就是data,stores,

data即当前时刻Model的数据

stores即定义数据源,可以定义多个数据源,personStore数据源的引用别名,可以通过grid.getStore("store引用名")来获取这里的数据源,后面的type是PersonStore定义的别名,即表示这里的Store是哪个类的实例。如果有多个store你可以这样:
stores: {
    aaa: {type: ""xx.xxxx.AA""},

    bbb: {type: ""xx.xxxx.BB""}

}

外部通过getStore("aa"),getStore("bb")这样来获取Store对象,后面的xx.xxx.AA是Store类的完整类路径(包含命名空间)

 

至于formulas是里定义的是一些函数用于绑定按钮禁用状态。

 

app/view/PersonFormPanel.js

Ext.define("MyApp.view.PersonFormPanel", {
	extend: "Ext.form.Panel",
	alias: "widget.personFormPanel",
	requires: [
	    "Ext.form.field.Number",
        "MyApp.controller.PersonController",
        "MyApp.viewmodel.PersonViewModel"
	],
	controller: "personController",
	viewModel: {
		type: "personViewModel"
	},
    publishes: ["currentPerson"],
	/**自动生成get/set*/
	config: {
	    currentPerson: null
	},

	bind : {
    	currentPerson: "{currentPerson}",
    	title: "<strong>{currentPerson.personName}</strong>"
	},
	bodyPadding: 10,
	defaultType: "textfield",
	defaults: {
		anchor: "100%",
		selectOnFocus: true
	},
	header: {
	    title: "Person Form",
	    padding: "4 9 5 9",
	    items: [
	        {
	        	text: "撤消",
	        	xtype: "button",
	        	itemId: "reject",
	        	handler: "onFormButtonClick",
	        	tooltip: "撤消重填",
	        	disabled: true,
	        	margin: "0 0 0 15",
	        	bind: {
	        		disabled: "{!dirty}"
	        	}
	        },
	        {
	        	text: "提交",
	        	xtype: "button",
	        	itemId: "commit",
	        	handler: "onFormButtonClick",
	        	tooltip: "提交",
	        	disabled: true,
	        	margin: "0 0 0 15",
	        	bind: {
	        		disabled: "{!dirty}"
	        	}
	        }
	    ]
	},
	items: [
		{
			name: "id",
			hidden: true,
			fieldLabel: "",
			bind: {
				value: "{currentPerson.id}"
			}
		},
	    {
	    	fieldLabel: "姓名",
	    	//disabled: true,
	    	bind: {
	    		value: "{currentPerson.personName}",
	    		disabled: "{!currentPerson}"
	    	}
	    },
	    {
	    	fieldLabel: "年龄",
	    	//disabled: true,
	    	bind: {
	    		value: "{currentPerson.age}",
	    		disabled: "{!currentPerson}"
	    	}
	    }
    ],
    height: 310
});

 PersonFormPanel和PersonGridPanel代码差不多,唯一就是Grid需要绑定Store数据源。

 

app/store/PersonStore.js

Ext.define("MyApp.store.PersonStore", {
    extend : "Ext.data.Store",
    requires: ["MyApp.model.PersonModel"],
    model: 'MyApp.model.PersonModel',
    alias: "store.personStore",
    storeId: "personStore",
    pageSize: 10,
    proxy: {
        type: 'ajax',
        url: MyApp.util.AppUtil.basePath + 'person.json',
        reader: { rootProperty: 'items', totalProperty: 'total' }
    },
    reader: {type: 'json'},
    sorters: [{
        property: 'id',
        direction: 'asc'
    }],
    autoLoad: true,
    isDirty: function() {
    	var dirty = this.getModifiedRecords().length;
    	dirty = dirty || this.getNewRecords().length;
    	dirty = dirty || this.getRemovedRecords().length;
    	return !!dirty;
    }
}); 

 Store就没有什么好说的,关键点就是配置Model类和proxy,proxy数据代理那里我为了简便起见,就没有编写访问数据库代码了,而仅仅是访问一个json文件,store需要的数据都以json字符串的形式定义在person.json文件里。Store是依赖于Model的,所以requires里需要引入PersonModel类。

 

下面贴出person.json里定义的测试数据:

webContent\person.json

{
    "total": 12,
    "items": [
        {
        	"id": 1,
            "personName": "益达1",
            "age": 28
        },
        {
        	"id": 2,
            "personName": "益达2",
            "age": 28
        },
        {
        	"id": 3,
            "personName": "益达3",
            "age": 28
        },
        {
        	"id": 4,
            "personName": "益达4",
            "age": 28
        },
        {
        	"id": 5,
            "personName": "益达5",
            "age": 28
        },
        {
        	"id": 6,
            "personName": "益达6",
            "age": 28
        },{
        	"id": 7,
            "personName": "益达7",
            "age": 28
        },
        {
        	"id": 8,
            "personName": "益达8",
            "age": 28
        },
        {
        	"id": 9,
            "personName": "益达9",
            "age": 28
        },
        {
        	"id": 10,
            "personName": "益达10",
            "age": 28
        },
        {
        	"id": 11,
            "personName": "益达11",
            "age": 28
        },
        {
        	"id": 12,
            "personName": "益达12",
            "age": 28
        }
    ]
}

 PersonModel就是一个普通实体类,就好比Java里的一个普通的JavaBean,仅仅是一些类属性声明;

app/model/PersonModel.js

Ext.define("MyApp.model.PersonModel", {
    extend : "Ext.data.Model",  
    fields : [
    	{name: 'id', type: 'int'},
	    {name: 'personName', type: 'string'},
	    {name: 'age', type: 'int'}
    ]
}); 

 

编写app.js来创建ExtJS的Application实例对象来运行我们的应用程序,这个文件存放路径没有什么规范约束,不像MVVC模式那样,controller类必须放controller目录下,Store类必须放store目录下。

webContent\app.js

Ext.Loader.setConfig({
	enabled : true
});
Ext.Loader.setPath({
	'Ext.ux' : 'extjs/ux',
	'MyApp.util' : 'app/util'
});

/**
 * 加载ExtJS插件文件
 */
Ext.require(
	[
		'Ext.ux.PageSizePaging',
		'MyApp.util.AppUtil'
	]
);
Ext.application({
    requires: ['Ext.container.Viewport','MyApp.view.Viewport'],
    //项目名称简称
    name: 'MyApp',
    appFolder: 'app',
    autoCreateViewport: true,
    
    launch: function () {
    	//Ext.create('MyApp.view.Viewport');
    }
});

 OK,最后新建一个JSP页面,测试一把,就完事儿了。

webContent\index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
request.setAttribute("basePath", basePath);
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>ExtJS5-MVVC设计模式学习</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!-- 加载ExtJS5  默认的经典蓝主题皮肤样式文件 -->
	<link href="${basePath}extjs/theme/ext-theme-classic/ext-theme-classic-all.css" rel="stylesheet" type="text/css"/>
	<script type="text/javascript" src="${basePath}extjs/bootstrap.js"></script>
	<script type="text/javascript" src="${basePath}extjs/ext-locale-zh_CN.js"></script>
	<script type="text/javascript" src="${basePath}app.js"></script> 
	
  </head>
  
  <body>
      
  </body>
</html>

 启动tomcat部署我们的测试项目,如图


 

 

启动Tomcat,打开浏览器,输入http://localhost:8080/extjs5-mvvc/访问页面,你将会看到如图效果:


 OK,今天就写到这儿,如果有什么问题请加我Q-Q:736031305,

或者加裙:一起交流学习

 

 

  • 大小: 20.5 KB
  • 大小: 27.3 KB
  • 大小: 62.4 KB
  • 大小: 348.8 KB
  • 大小: 121 KB
  • 大小: 160.8 KB
  • 大小: 35.4 KB
  • 大小: 67 KB
  • 大小: 156.5 KB
  • 大小: 6 KB
分享到:
评论

相关推荐

    Extjs 5 学习笔记

    Extjs 5 学习笔记,在网上下载整理好的。

    extJs 2.1学习笔记

    目录 1. ExtJs 结构树 2 2. 对ExtJs的态度 3 3. Ext.form概述 4 4. Ext.TabPanel篇 5 5. Function扩展篇 7 6. Ext.data.Store篇 10 7. Ext.data.JsonReader篇一 12 ...28. extJs 2.0学习笔记(ext.js篇) 77

    Extjs4.0学习笔记

    Extjs4.0学习笔记,以下是部分介绍: xtjs4,创建Ext组件有了新的方式,就是Ext.create(....),而且可以使用动态加载JS的方式来加快组件的渲染,我们再也不必一次加载已经达到1MB的ext-all.js了,本文介绍如何在EXTJS4...

    extjs4.x学习笔记

    从ExtJs5开始则引入了MVVC架构。 从网上资料来看,多数都是停留在ExtJs3,而4+以上版本跟3有比较大的差异,增加了过度难度,这些实例和源代码有利于从3迁移到4的,从而更好的跟5衔接。"&gt;ExtJs已经升级到了5.0了,...

    ExtJs入门实例

    5. ExtJs2.0学习系列(5)--Ext.FormPanel之第二式 6. ExtJs2.0学习系列(6)--Ext.FormPanel之第三式(ComboBox篇) 7. ExtJs2.0学习系列(7)--Ext.FormPanel之第四式(其他组件示例篇) 8. ExtJs2.0学习系列(8)--Ext....

    ExtJS4ExtJS5MD5 加密

    适用于ExtJS4、ExtJS5 MD5加密算法!

    ExtJs2.0学习系列

    ExtJs2.0学习系列ExtJs2.0学习系列

    Extjs4.1 小例子(适合extjs初学者学习使用)

    Extjs4.1 小例子(适合extjs初学者学习使用).直接导入myeclipse即可

    extjs资料以及extjs学习指南

    extjs资料以及extjs学习指南,extjs资料以及extjs学习指南

    extjs4学习文档

    extjs4的学习文档,比较完整,跟大家分享下

    ExtJS 3.0 学习资源

    ExtJS 3.0 学习资源,有兴趣的 可以下载看看!!!!

    ExtJs2.0学习系列大全.rar

    ExtJs2.0学习系列大全 共15个word文档,大部分介绍都在里面了

    ExtJs2.0学习系列.CHM

    关于ext学习的资料,有些例子 ExtJs2.0学习系列.CHM

    EXTJS4.2学习入门教程

    EXTJS4.2学习入门教程 EXTJS4.2学习入门教程 EXTJS4.2学习入门教程

    Extjs4.0学习手册、入门教程详解学习

    Extjs4.0学习手册、入门教程详解学习 Extjs4.0学习手册、入门教程详解学习 Extjs4.0学习手册、入门教程详解学习

    extjs入门之组件学习

    extjs入门学习,各个组件的使用,包括Observable、Observable、BoxComponent、Container、Panel、Viewport及Window...

    extJs3.0学习资料以及官方实例

    extJs3.0学习资料以及官方实例 extJs3.0学习资料以及官方实例 extJs3.0学习资料以及官方实例

    ExtJs学习笔记,共30讲

    ExtJs学习笔记,共30讲 1. ExtJs 结构树 2 2. 对ExtJs的态度 3 3. Ext.form概述 4 4. Ext.TabPanel篇 5 5. Function扩展篇 7 6. Ext.data.Store篇 10 7. Ext.data.JsonReader篇一 12 8. Ext.data.JsonReader篇二 15 ...

Global site tag (gtag.js) - Google Analytics