本文源码地址:http://download.csdn.net/detail/xz2001/4944407
一、介绍与下载
整个框架的整合中,ExtJS比较简单,与NHibernate、Spring.NET几乎没有什么关系,唯一需要的是JSON或XML格式数据。
在写这篇文件时,ExtJS的最新版是4.0.7,本文也就采用这个版本。
ExtJS4.0与2、3有明显的区别,3.x向4.x的升级,不是2.x向3.x升级那样简单,4.x中整体性能有明显提升。另外,4.x中使用了流行的MVC模式,还有懒加载的机制等等。
ExtJS官方下载地址:http://www.sencha.com/products/extjs/download/
二、安装部署
为了清晰项目结构,个人喜欢把所有js、css、img以及第三方客户端框架都放在Web项目的Content目录中。其中的css、主题暂时不需要,可以先清空,然后把extjs解压后的目录整个复制进来。如下图:
这里说一下,由于extjs框架中的文件太多,复制进来的时候特别慢,我的电脑等了估计有5分钟左右。所以,可以把extjs精简一下,比如doc、examples等目录删除,另外,也可以在VS解决方案资源管理器中排除extjs整个目录,这样以后会快很多。
三、项目结构
既然使用了ExtJS4.o,肯定要用4.0中的MVC开发模式。在编写之前,先看下我的目录结构:
有过MVC开发经验的应该一眼明白。事实上,MVC更多的是一种约定,比如:视图放在模块名称的目录下,系统会自动来这个目录下搜索。至于能不能放到别处,本人没仔细研究,不过,ASP.NET MVC中的视图是可以的,只要重写“WebFormViewEngine”类,详细看这里:http://blog.csdn.net/xz2001/article/details/7031756
另外,看一下Area的结构:
这里大概说几点:
1、ExtJS4.0中所有的资源文件是动态加载的,具体可以了解"Ext Loader"加载机制;
2、ExtJS4.0项目是以模块为单位进行开发,在ExtJS中是以命名空间体现的;
3、每个模块的入口文件是app.js,app中要声明需要使用的控制器;
4、控制器中要声明使用到的View、Model以及Store。理论上说,应该把所有事件的处理放在Controller中,就如在Struts、ASP.NET MVC中开发一样,事件应该由控制器捕获和处理,View仅仅负责渲染控件、显示数据;
5、Model与Struts、ASP.NET MVC中的实体类差不多,但不同于领域模型中的领域对象,这里的Model仅仅含有数据,不含有任何方法;
6、Store在ExtJS中扮演着数据源与Model之间的桥梁,通过与Proxy对象进行数据的CURD操作;
7、4.0中的视图负责控件的渲染,实事上视图中的代码功能与之前的版本很相似。
四、编写代码
由于用到的js文件较多,这里我就直接上代码了,不清楚的对应上面的截图看一下。
1、app.js
Ext.application({
name: 'AM',
appFolder: '/Content/Manage/app',
controllers: [
'Menu',
'User'
],
autoCreateViewport: true
});
注:这里使用了2个Controller,需要在这里注册下,并自动创建Viewport组件,会自动从"appFolder"参数指定的目录中搜索。
2、app/Viewport.js
Ext.define('AM.view.Viewport', {
extend: 'Ext.container.Viewport',
requires: [
'AM.view.Head',
'AM.view.Menu',
'AM.view.Foot',
'AM.view.Body'
],
layout: 'border',
items: [{
region: 'north',
height: 45,
xtype: 'head'
},{
region: 'west',
width: 225,
xtype: 'menu'
},{
region: 'south',
height: 25,
xtype: 'foot'
},{
region: 'center',
xtype: 'body'
}]
});
注:requires声明必须先引入的类,其共有四个子元素,分别布局在上、下、左和中间,下面一个一个看。
3、app/Menu.js
该控件布局在左侧,是项目的管理菜单。
Ext.define('AM.view.Menu', {
extend : 'Ext.panel.Panel',
alias : 'widget.menu',
requires : [ 'AM.view.menu.Tree' ],
margins : '0 0 0 5',
split : true,
collapsible : true,
header : true,
title : '管理菜单',
initComponent : function() {
this.items = [{ xtype: 'menuTree' }];
this.callParent(arguments);
}
});
注:AM.view.Menu中添加了一个子元素,其xtype是“menuTree”,实际上是一个Tree对象。稍后会有这个对象。
4、app/Head.js
布局在上方,一般用来显示项目名称和一些快捷导航,这里为了方便没有显示这么复杂,就显示“加载中...”
Ext.define('AM.view.Head', {
extend : 'Ext.panel.Panel',
alias : 'widget.head',
margins : '5 5 0 5',
split : true,
header : false,
collapsible : true,
initComponent : function() {
this.html = "<div>加载中...</div>";
this.callParent(arguments);
}
});
5、app/Foot.js
显示在最下方,一般可以用来显示当前登录的管理员信息,这里为了方便,也没有显示。
Ext.define('AM.view.Foot', {
extend : 'Ext.panel.Panel',
alias : 'widget.foot',
margins : '5 5 5 5',
header : false,
frame : true,
collapsible : true,
initComponent : function() {
this.callParent(arguments);
}
});
6.app/Body.js
管理模块的主体部分,根据管理的模块不同,即时渲染相应的控件来显示数据、修改数据、添加、删除数据等操作。这里只是加载了一个xtype为'userList',显示的是用户列表,后面会说到这个控件。
Ext.define('AM.view.Body', {
extend : 'Ext.panel.Panel',
alias : 'widget.body',
requires : [ 'AM.view.user.List' ],
margins : '0 5 0 0',
border : 0,
header : false,
baseCls : 'x-plain',
collapsible : true,
initComponent : function() {
this.items = [ {
xtype: 'userList'
} ];
this.callParent(arguments);
}
});
7、app/controller/Menu.js
这个是左侧菜单的控制器,处理左侧菜单的事件以及事件的处理,这里只是在单击菜单时写入了一条日志。
Ext.define('AM.controller.Menu', {
extend: 'Ext.app.Controller',
stores: ['Menu'],
views: ['menu.Tree'],
init: function() {
this.control({
'menuTree': {
itemclick: this.selectItem
}
});
},
selectItem: function(){
console.log('click item');
}
});
8、app/store/Menu.js
左侧菜单的Store对象,负责处理数据。
Ext.define('AM.store.Menu', {
extend: 'Ext.data.TreeStore',
autoLoad : true,
proxy: {
type: 'ajax',
api: {
read: '/Content/Manage/data/menus.js'
},
reader: {
type: 'json',
root: 'childrens'
}
},
sorters: [{
property: 'leaf',
direction: 'ASC'
}],
root: {
nodeType: 'async',
text: 'Ext JS',
expanded: true
}
});
这里调用的是静态数据,页面URL是"/Content/Manage/data/menus.js",其数据如下:
{childrens:[
{id:'01',text:'数据管理',childrens:[
{id:'01-01',text:'新闻管理',leaf:true},
{id:'01-02',text:'分类管理',leaf:true},
{id:'01-03',text:'公告管理',leaf:true}
]},
{id:'02',text:'系统配置',leaf:true}
]}
9、app/view/menu/Tree.js
左侧菜单的视图文件,负责渲染组件:
Ext.define('AM.view.menu.Tree' ,{
extend: 'Ext.tree.Panel',
alias : 'widget.menuTree',
header : false,
border : 0,
store: 'Menu',
rootVisible: false
});
现在来看用户的相关对象,先看控制器:
10、app/controller/User.jsExt.define('AM.controller.User', {
extend: 'Ext.app.Controller',
stores: ['User'],
views: ['user.List'],
models: ['User']
});
由于用户数据需要Model对象,来看下:
11、app/model/User.js
Ext.define('AM.model.User', {
extend : 'Ext.data.Model',
fields : [ 'name', 'email' ]
});
再看下Store:
12、app/store/User.js
Ext.define('AM.store.User', {
extend: 'Ext.data.Store',
model: 'AM.model.User',
autoLoad: true,
sorters: [{
property: 'code',
direction: 'ASC'
}],
proxy: {
type: 'ajax',
api: {
read: '/Content/Manage/data/userList.js'
},
reader: {
type: 'json',
root: 'users',
successProperty: 'success'
}
},
listeners:{
update: function(store, record){
console.log('listeners update. name='+record.get('name'));
}
}
});
这个Store稍微复杂些,使用了Proxy对象,复杂与后台进行数据交互。这里不详解,以后的文章都会一一细说。
这里只说/Content/Manage/data/userList.js返回的数据:
{
success: true,
users: [
{id: 1, name: '崔北为', email: 'cbw@cbw.com'},
{id: 2, name: '李军', email: 'lj@lj.com'},
{id: 3, name: '钱云禄', email: 'qyl@qyl.com'}
]
}
13、app/view/user/List.js
Ext.define('AM.view.user.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.userList',
title : 'All Users',
store: 'User',
columns: [
{header: 'Name', dataIndex: 'name', flex: 1},
{header: 'Email', dataIndex: 'email', flex: 1}
],
buttons:[
{text:'add',action:'add'},
{text:'delete',action:'delete'}
]
});
到此,所有的js文件全部搞定,可以测试运行了。
五、运行预览