今天工作的主要内容就是菜单的实现以及点击菜单后在右边内容区打开一个新的Panel。本篇文章的内容主要包括两个方面,Extjs4 Tree及Extjs4 tabPanel。
在Extjs应用中实现菜单,无疑Tree是最好的选择,将菜单项直接写到Tree中,也未尝不可,但后期的维护会非常麻烦,那么最好的选择就是异步获取菜单节点,这样既有利于后期维护,也可以节省JS代码的编写量。
实现异步加载,那必须要有数据库和服务端程序。管理系统使用的是ACCESS数据库。在数据库中建立Menu表,表一共有4个字段,分别是ID、MenuName、ParentID和cls. ID:自增长类型
MenuName:代表菜单的名字。ParentID ParentID:父ID
cls:样式名(这个需要在样式表中体现出来,才会有效果)
数据库有了,接下来就是服务端的编写,其实JS框架的好处在于服务端无论用什么都可以,这里我就使用ASP来实现了。由于服务端涉及的方面比较多,较多代码帖出来会比较乱,这里只说下返回的形式。
菜单需要的JSON数据格式如下:
[{\"text\":\"管理员管理\[{\"text\":\"管理员管理\
这里注意一点:由于整棵菜单树都是异步获取的,所以节点并不需要递归,而树的node.id就是JSON数据中的ID。点击父节点展开子节点的时候,发送的数据也是node.id,这样正好解决获取子节点的问题。所有根节点的ParentID都为0。那么第一次加载菜单的时候,正好可以获取所有的根节点了。
重点,菜单树的数据源编写,根据MVC原则,在app目录下建立store文件夹,这个文件夹下放置所有的获取数据的js文件,在store文件夹下建立Menus.js,编写如下代码:
Ext.define('SMS.store.Menus',{ extend: 'Ext.data.TreeStore', root: {
expanded: true },
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp' }
})
Ext.define('SMS.store.Menus',{ extend: 'Ext.data.TreeStore', root: {
expanded: true
},
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp' }
})
然后修改view文件夹下的Menu.js文件:
Ext.define('SMS.view.Menu',{ extend: 'Ext.tree.Panel', alias: 'widget.smsmenu',
requires:['SMS.store.Menus'],
initComponent : function(){ Ext.apply(this,{
id: 'menu-panel', title: '系统菜单', iconCls:'icon-menu', margins : '0 0 -1 1', region:'west', border : false, enableDD : false, split: true, width : 212, minSize : 130, maxSize : 300,
rootVisible: false, containerScroll : true, collapsible : true, autoScroll: false,
store:Ext.create('SMS.store.Menus'), });
this.callParent(arguments); } })
Ext.define('SMS.view.Menu',{ extend: 'Ext.tree.Panel', alias: 'widget.smsmenu',
requires:['SMS.store.Menus'], initComponent : function(){ Ext.apply(this,{ id: 'menu-panel', title: '系统菜单', iconCls:'icon-menu',
margins : '0 0 -1 1', region:'west', border : false, enableDD : false, split: true, width : 212, minSize : 130, maxSize : 300,
rootVisible: false, containerScroll : true, collapsible : true, autoScroll: false,
store:Ext.create('SMS.store.Menus'), });
this.callParent(arguments); } })
如此,当页面打开时,就会自动加载菜单项了。但是目前来说,点击菜单的任何节点都没有任何作用,那么由于整个项目都要使用Extjs来完成,那么必须要实现点击节点在右边内容区显示相应的Grid或Panel等等。下面,我们编写代码实现这个功能。
原理:当点击菜单树的节点时,先要判断要打开的组件是否存在,如果存在,则在右边内容区激活当前组件,如果不存在,则创建一个组件,然后在右边内容区增加一个组件。当然。这里为了通用性更高,创建出的组件一律按panel为准。为了实现在右边内容区的tabPanel上增加或删除对应的table,根据分离原则,我们需要在控制器上完成该操作,接下来,我们在controller文件夹下建立Menu.js文件,Menu.js会完成这一系列的工作,具体代码如下:
Ext.define('SMS.controller.Menu',{ extend: 'Ext.app.Controller', refs:[
{ref: 'smsmenu',selector: 'smstablepanel'}, {ref: 'tabPanel',selector:'smstablepanel'} ],
init:function(){ this.control({
'smsmenu': {
itemmousedown: this.loadMenu } }) },
loadMenu:function(selModel, record){ if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){ panel ={
title: 'New Tab ' + record.get('id'), iconCls: 'tabs',
html: 'Tab Body ' + record.get('id') + '
', closable: true }
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-panel\"); main.setActiveTab(panel); } }
},
openTab : function (panel,id){
var o = (typeof panel == \"string\" ? panel : id || panel.id); var main = Ext.getCmp(\"content-panel\"); var tab = main.getComponent(o); if (tab) {
main.setActiveTab(tab);
} else if(typeof panel!=\"string\"){ panel.id = o;
var p = main.add(panel); main.setActiveTab(p); } }
})
Ext.define('SMS.controller.Menu',{ extend: 'Ext.app.Controller', refs:[
{ref: 'smsmenu',selector: 'smstablepanel'}, {ref: 'tabPanel',selector:'smstablepanel'} ],
init:function(){ this.control({
'smsmenu': {
itemmousedown: this.loadMenu } }) },
loadMenu:function(selModel, record){ if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){ panel ={
title: 'New Tab ' + record.get('id'), iconCls: 'tabs',
html: 'Tab Body ' + record.get('id') + '
', closable: true }
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-panel\"); main.setActiveTab(panel); }
}
},
openTab : function (panel,id){
var o = (typeof panel == \"string\" ? panel : id || panel.id); var main = Ext.getCmp(\"content-panel\"); var tab = main.getComponent(o); if (tab) {
main.setActiveTab(tab);
} else if(typeof panel!=\"string\"){ panel.id = o;
var p = main.add(panel); main.setActiveTab(p); } }
})
关键点:refs和this.control,refs在官方API中没有找到其解释,网上查了下,对该属性的解释是:凡是component都可以使用该属性在它的归属容器及归属容器的父节点中注入一个对该属性的引用名称。有了该引用名,和该组件有共同父节点的组件就可以比较方便的引用该组件。
而this.control则很容易理解了,即通过Ext.ComponentQuery为选定的组件添加监听。上面代码为我们的菜单节点添加了一个事件loadMenu,loadMenu中,先获取对应的panel,如果该panel不存在,则使用openTab方法在内容区的tabPanel上增加一个panel,如果存在,则激活该panel。而panel不存在的话,这里只简单的创建了一个panel,并没有任何意义,下篇文章,我们将详细讨论这个问题。
最后,我们需要修改app.js,将我们创建的控制器加载进来。app.js:
Ext.Loader.setConfig({enabled: true}); Ext.application({ name: 'SMS',
appFolder: 'app', autoCreateViewport:true, controllers: [ 'Menu' ] });
Ext.Loader.setConfig({enabled: true}); Ext.application({ name: 'SMS',
appFolder: 'app',
autoCreateViewport:true, controllers: [ 'Menu' ] });
这里唯一修改的地方就是将Main改为了Menu。后面,我们会逐步完善。 今天的工作比较多,整理下今日的工作内容:
1、在app目录下建立了store文件夹,并建立了Menus.js文件,这个文件负责菜单数据的加载。
2、修改了view文件夹下的Menu.js,使其可以加载菜单数据。
3、在controller文件夹下建立了Menu.js,使其可以在内容区的tabPanel上增加新的panel组件。
4、修改app.js,使其可以使菜单项正常使用。
Extjs4开发笔记(二)——框架的搭建
谈
(2011-12-29 20:14:44)
转载
▼
标签: 杂分类: extjs4
本章内容增加了一些图片及CSS文件,目的是为了美化整个界面。增加的CSS文件: 注意事项:layout、region的使用,如果没有看API及相关文档的话,那么面对错误对话框的时候,还不知道是怎么回事。
本文将main.js放到了/app/controller文件夹下,这将是整个项目的主体文件。 而头部、菜单、内容区及底部则完全分离成4个JS文件,我们将先实现这几个文件的基础功能,之后我们会慢慢完善这些组件。而整个页面的填充,也使用一个JS文件来完成。也许有人会问,这么多文件,是不是要都在index.html中引入啊。这样想的话,就错了哦。因为我们使用的是Extjs4,所以我们一定要使用Extjs4 动态加载功能。 先来看下自定义CSS(sytle.css):
#header { background: #7F99BE url(/images/layout-browser-hd-bg.gif) repeat-x
center;}
#header h1 {font-size: 16px;color: #fff;padding: 3px 10px; font-family:\"微软雅黑\黑体\ .tabs{}
.tabs{background-image:
url(../images/menuPanel/bulletin_manager.gif) !important;}
.manage{background-image: url(../images/menuPanel/admin.gif) !important;} .home{background-image: url(../images/home.gif) !important; line-height:30px;} .icon-menu{background-image: url(../images/menuPanel/sys.gif) !important;} 图片文件夹就不放上来了。从以前的项目中拷贝了一些比较靠谱的图片,大家完全可以自己去下载一些ICON图标文件而为己所用。
搭建的框架是经典的EXTJS布局模式,如图所示:
首先,我们建立index.html和app.js,index.html代码为:
Transitional//EN\"
App.js:
Ext.Loader.setConfig({enabled: true}); Ext.application({ name: 'SMS', appFolder: 'app', controllers: [ 'Main' ] }); 稍做解释:
Ext.Loader.setConfig({enabled: true});//意思是开启Ext.Loader。Ext.Loader是动态加载的核心哦。。 Ext.appliction({...});看字面意思吧,不解释。
配置中的name,我理解为是Extjs3.x、Extjs2.x中的命名空间。 appFolder,应用文件夹名字。
controllers,控制单元的名字,这里我们定义为Main。那么根据Extjs4动态加载的要求,我们需要在/app/controllers文件夹下建立Main.js文件,作为控制单元。有关Extjs4动态加载机制,请参考:www.mhzg.net/a/20117/2011721040290.html Main.js:
Ext.define('SMS.controller.Main',{ extend: 'Ext.app.Controller', init : function(){ } })
这里的Main.js中只是定义了Main这个类,且继承了Ext.app.Controller,其余都没有写。
看到这里,会有人很奇怪了,index.html中引入了app.js,而app.js只是创建了Main这个类,但Main.js什么都没有,那么页面中为什么会显示出框架页面呢?这也是最多人所疑惑的。下面我来解释下这个问题。所有的原因就在于app.js这个文件中,app.js文件定义了Ext.application。而Ext.application中有个属性是autoCreateViewport ,这个属性是Boolean类型,如果值为true,那么Extjs4会自动加载view/Viewport.js文件,如果值为flase,那么必须要自己去创建一个View,这就是为什么app.js和Main.js文件都没有写相关代码,也会有界面出现。
整理下思路,由于Extjs4自动加载了view/Viewport.js,而Viewport.js文件包含了头部、菜单、内容区及底部这4个组件,那么我们必须先完成这4个文件的编写,同样,由于这4个文件是界面型的,我们将这4个文件都放到view文件夹下。 view文件夹下共5个JS文件,分别为:
Header.js、Menu.js、South.js、TabPanel.js及Viewport.js 这5个js文件的作用,我们一一介绍。
5个js文件都包含了Ext.applyIf、callParent。由于篇幅问题,Ext.applyIf、callParent等方法,请参考Extjs4相关API。
Header.js:这个是头部,也就是深蓝色底子。白色字体,那块,上面写着员工管理系统。 代码为:
Ext.define('SMS.view.Header', { extend: 'Ext.Component', initComponent: function() { Ext.applyIf(this, { xtype: 'box', cls: 'header', region: 'north',
html: '
this.callParent(arguments); } }); Menu.js:
Ext.define('SMS.view.Menu',{ extend: 'Ext.tree.Panel', initComponent : function(){ Ext.apply(this,{ id: 'menu-panel', title: '系统菜单',
iconCls:'icon-menu', margins : '0 0 -1 1', region:'west', border : false, enableDD : false, split: true, width : 212, minSize : 130, maxSize : 300, rootVisible: false, containerScroll : true, collapsible : true, autoScroll: false });
this.callParent(arguments); } })
TreePanel并没有加载菜单项,关于Extjs4 Tree,我们后面会介绍。 TabPanel.js:
Ext.define('SMS.view.TabPanel',{ extend: 'Ext.tab.Panel', initComponent : function(){ Ext.apply(this,{ id: 'content-panel', region: 'center', defaults: { autoScroll:true, bodyPadding: 10 },
activeTab: 0, border: false, //plain: true, items: [{ id: 'HomePage', title: '首页',
iconCls:'home', layout: 'fit' }] });
this.callParent(arguments); } })
South.js:
Ext.define('SMS.view.South',{ extend: 'Ext.Toolbar', initComponent : function(){ Ext.apply(this,{ id:\"bottom\ //frame:true, region:\"south\ height:23,
items:[\"当前用户:Guest\技术支持:http://www.mhzg.net \"] });
this.callParent(arguments); } })
Ext.define('SMS.view.South',{ extend: 'Ext.Toolbar', initComponent : function(){ Ext.apply(this,{ id:\"bottom\ //frame:true, region:\"south\ height:23,
items:[\"当前用户:Guest\技术支持:http://www.mhzg.net \"] });
this.callParent(arguments);
} })
文件都创建好了。我们进行最后一部,布局。
Viewport.js:
Ext.define('SMS.view.Viewport',{ extend: 'Ext.Viewport', layout: 'fit', hideBorders: true, requires : [
'SMS.view.Header', 'SMS.view.Menu', 'SMS.view.TabPanel', 'SMS.view.South' ],
initComponent : function(){ var me = this; Ext.apply(me, { items: [{ id:'desk', layout: 'border', items: [
Ext.create('SMS.view.Header'), Ext.create('SMS.view.Menu'), Ext.create('SMS.view.TabPanel'), Ext.create('SMS.view.South') ] }] });
me.callParent(arguments); } })
Ext.define('SMS.view.Viewport',{ extend: 'Ext.Viewport', layout: 'fit',
hideBorders: true, requires : [
'SMS.view.Header', 'SMS.view.Menu', 'SMS.view.TabPanel', 'SMS.view.South' ],
initComponent : function(){ var me = this; Ext.apply(me, { items: [{
id:'desk',
layout: 'border', items: [
Ext.create('SMS.view.Header'), Ext.create('SMS.view.Menu'), Ext.create('SMS.view.TabPanel'), Ext.create('SMS.view.South') ] }] });
me.callParent(arguments); } })
重点:requires属性,这个我理解为创建引用。稍懂编程语言的人应该都明白。但是光引用还不够,我们还需要去实例化它,也就是Ext.create。至此,我们的框架已经顺利搭建完毕。
今天的工作也就是这么多,搭建完框架之后,会慢慢丰富整个系统。本来想连菜单的树也完成,最后想了想,这工作还是留到明天吧。因为树涉及到了异步获取,需要有服务端程序,今天弄好框架之后,把服务端代码写好了,明天来完成这棵树的实现吧。
需要注意的一点,在extjs4中,只要定义了布局为border,那么他的items中必须要有region: 'center'的组件,否则将会提示错误。貌似在extjs3.x甚至是以前的版本都没发现有这样的要求,因为这个,费了老大的劲才调整过来,再一看,代码全部变了,已经跟extjs3.x的风格完全不同了。令人欣喜的是,现在的代码完全符合extjs4的风格,也完全符合我的预期。
好了,本章内容就结束了。下篇文章,将实现菜单的功能。敬请期待。
Extjs4开发笔记(三)——菜单的实现
(2011-12-29 20:27:22)
标签: 杂谈
转载
▼
分类: extjs4
今天工作的主要内容就是菜单的实现以及点击菜单后在右边内容区打开一个新的Panel。本篇文章的内容主要包括两个方面,Extjs4 Tree及Extjs4 tabPanel。
在Extjs应用中实现菜单,无疑Tree是最好的选择,将菜单项直接写到Tree中,也未尝不可,但后期的维护会非常麻烦,那么最好的选择就是异步获取菜单节点,这样既有利于后期维护,也可以节省JS代码的编写量。
实现异步加载,那必须要有数据库和服务端程序。管理系统使用的是ACCESS数据库。在数据库中建立Menu表,表一共有4个字段,分别是ID、MenuName、ParentID和cls. ID:自增长类型
MenuName:代表菜单的名字。ParentID ParentID:父ID
cls:样式名(这个需要在样式表中体现出来,才会有效果)
数据库有了,接下来就是服务端的编写,其实JS框架的好处在于服务端无论用什么都可以,这里我就使用ASP来实现了。由于服务端涉及的方面比较多,较多代码帖出来会比较乱,这里只说下返回的形式。
菜单需要的JSON数据格式如下:
[{\"text\":\"管理员管理\[{\"text\":\"管理员管理\
这里注意一点:由于整棵菜单树都是异步获取的,所以节点并不需要递归,而树的node.id就是JSON数据中的ID。点击父节点展开子节点的时候,发送的数据也是node.id,这样正好解决获取子节点的问题。所有根节点的ParentID都为0。那么第一次加载菜单的时候,正好可以获取所有的根节点了。
重点,菜单树的数据源编写,根据MVC原则,在app目录下建立store文件夹,这个文件夹下放置所有的获取数据的js文件,在store文件夹下建立Menus.js,编写如下代码:
Ext.define('SMS.store.Menus',{ extend: 'Ext.data.TreeStore', root: {
expanded: true },
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp' }
})
Ext.define('SMS.store.Menus',{ extend: 'Ext.data.TreeStore', root: {
expanded: true },
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp' }
})
然后修改view文件夹下的Menu.js文件:
Ext.define('SMS.view.Menu',{ extend: 'Ext.tree.Panel', alias: 'widget.smsmenu',
requires:['SMS.store.Menus'], initComponent : function(){ Ext.apply(this,{
id: 'menu-panel', title: '系统菜单', iconCls:'icon-menu', margins : '0 0 -1 1', region:'west', border : false, enableDD : false, split: true, width : 212, minSize : 130, maxSize : 300,
rootVisible: false, containerScroll : true,
collapsible : true, autoScroll: false,
store:Ext.create('SMS.store.Menus'), });
this.callParent(arguments); } })
Ext.define('SMS.view.Menu',{
extend: 'Ext.tree.Panel', alias: 'widget.smsmenu',
requires:['SMS.store.Menus'], initComponent : function(){ Ext.apply(this,{ id: 'menu-panel', title: '系统菜单', iconCls:'icon-menu', margins : '0 0 -1 1', region:'west', border : false, enableDD : false, split: true, width : 212, minSize : 130, maxSize : 300,
rootVisible: false, containerScroll : true, collapsible : true, autoScroll: false,
store:Ext.create('SMS.store.Menus'), });
this.callParent(arguments); } })
如此,当页面打开时,就会自动加载菜单项了。但是目前来说,点击菜单的任何节点都没有任何作用,那么由于整个项目都要使用Extjs来完成,那么必须要实现点击节点在右边内容区显示相应的Grid或Panel等等。下面,我们编写代码实现这个功能。
原理:当点击菜单树的节点时,先要判断要打开的组件是否存在,如果存在,则在右边内容区激活当前组件,如果不存在,则创建一个组件,然后在右边内容区增加一个组件。当然。这里为了通用性更高,创建出的组件一律按panel为准。为了实现在右边内容区的tabPanel上增加或删除对应的table,根据分离原则,我们需要在控制器上完成该操作,接下来,我们在controller文件夹下建立Menu.js文件,Menu.js会完成这一系列的工作,具体代码如下:
Ext.define('SMS.controller.Menu',{ extend: 'Ext.app.Controller', refs:[
{ref: 'smsmenu',selector: 'smstablepanel'}, {ref: 'tabPanel',selector:'smstablepanel'} ],
init:function(){
this.control({ 'smsmenu': {
itemmousedown: this.loadMenu } }) },
loadMenu:function(selModel, record){ if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){ panel ={
title: 'New Tab ' + record.get('id'), iconCls: 'tabs',
html: 'Tab Body ' + record.get('id') + '
', closable: true }
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-panel\"); main.setActiveTab(panel); } }
},
openTab : function (panel,id){
var o = (typeof panel == \"string\" ? panel : id || panel.id); var main = Ext.getCmp(\"content-panel\"); var tab = main.getComponent(o); if (tab) {
main.setActiveTab(tab);
} else if(typeof panel!=\"string\"){ panel.id = o;
var p = main.add(panel); main.setActiveTab(p); } } })
Ext.define('SMS.controller.Menu',{ extend: 'Ext.app.Controller', refs:[
{ref: 'smsmenu',selector: 'smstablepanel'}, {ref: 'tabPanel',selector:'smstablepanel'} ],
init:function(){
this.control({ 'smsmenu': {
itemmousedown: this.loadMenu } }) },
loadMenu:function(selModel, record){ if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){ panel ={
title: 'New Tab ' + record.get('id'), iconCls: 'tabs',
html: 'Tab Body ' + record.get('id') + '
', closable: true }
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-panel\"); main.setActiveTab(panel); }
}
},
openTab : function (panel,id){
var o = (typeof panel == \"string\" ? panel : id || panel.id); var main = Ext.getCmp(\"content-panel\"); var tab = main.getComponent(o); if (tab) {
main.setActiveTab(tab);
} else if(typeof panel!=\"string\"){ panel.id = o;
var p = main.add(panel); main.setActiveTab(p); } } })
关键点:refs和this.control,refs在官方API中没有找到其解释,网上查了下,对该属性的解释是:凡是component都可以使用该属性在它的归属容器及归属容器的父节点中注入一个对该属性的引用名称。有了该引用名,和该组件有共同父节点的组件就可以比较方便的引用该组件。
而this.control则很容易理解了,即通过Ext.ComponentQuery为选定的组件添加监听。上面代码为我们的菜单节点添加了一个事件loadMenu,loadMenu中,先获取对应的panel,
如果该panel不存在,则使用openTab方法在内容区的tabPanel上增加一个panel,如果存在,则激活该panel。而panel不存在的话,这里只简单的创建了一个panel,并没有任何意义,下篇文章,我们将详细讨论这个问题。
最后,我们需要修改app.js,将我们创建的控制器加载进来。app.js:
Ext.Loader.setConfig({enabled: true}); Ext.application({ name: 'SMS',
appFolder: 'app', autoCreateViewport:true, controllers: [ 'Menu'
] });
Ext.Loader.setConfig({enabled: true}); Ext.application({ name: 'SMS',
appFolder: 'app',
autoCreateViewport:true, controllers: [ 'Menu' ] });
这里唯一修改的地方就是将Main改为了Menu。后面,我们会逐步完善。 今天的工作比较多,整理下今日的工作内容:
1、在app目录下建立了store文件夹,并建立了Menus.js文件,这个文件负责菜单数据的加载。
2、修改了view文件夹下的Menu.js,使其可以加载菜单数据。
3、在controller文件夹下建立了Menu.js,使其可以在内容区的tabPanel上增加新的panel组件。
4、修改app.js,使其可以使菜单项正常使用。
Extjs4开发笔记(五)——动态grid
(2011-12-29 20:31:39)
标签: 杂谈
转载
▼
分类: extjs4
现在为止,框架有了,菜单有了。下一步工作就是实现每个菜单项目的数据显示,一般来讲,
我们都是固定思维,通过编写大量的grid来实现各种数据的显示,在网络上有一些案例,都是每一个菜单项目,写一个grid,而且点击树节点,都是通过判断或其他方法来创建相应
上一节我们说了如何实现登录,其地址是:Extjs4开发笔记(四)——实现登录功能。现在为止,框架有了,菜单有了。下一步工作就是实现每个菜单项目的数据显示,一般来讲,我们都是固定思维,通过编写大量的grid来实现各种数据的显示,在网络上有一些案例,都是每一个菜单项目,写一个grid,而且点击树节点,都是通过判断或其他方法来创建相应grid的实例,例如:
if(node.id==\"depmanage\"){
panel = new depManagePanel(); main.openTab(panel);
}else if(node.id==\"telmanage\"){ panel = new telManagePanel(); main.openTab(panel);
}else if(node.id==\"usermanage\"){ panel = new userManagePanel(); main.openTab(panel);
}else if(node.id==\"userlog\"){ panel = new operationPanel(); main.openTab(panel); }else{.....}}
if(node.id==\"depmanage\"){
panel = new depManagePanel(); main.openTab(panel);
}else if(node.id==\"telmanage\"){ panel = new telManagePanel(); main.openTab(panel);
}else if(node.id==\"usermanage\"){ panel = new userManagePanel(); main.openTab(panel);
}else if(node.id==\"userlog\"){ panel = new operationPanel(); main.openTab(panel); }else{.....}}
但是在很多优秀的案例中,也不排除动态生成gird的可能,至少目前为止,我没见过此种案例,可能是孤陋寡闻吧。。 在这个项目的开发过程中,我不断思考,是否能动态生成grid,使得工作一劳永逸。在查阅一些资料和自己动手尝试后,发现这样的办法可行,但前提是,需要服务端支持。单纯从开发角度来讲,我感觉这样的方法更加适合实际的开发。 在本节开始前,我们先来看一个问题。
树节点服务端返回的json数据:看了API之后,发现树的sotre返回json数据后,如果不
指定其fields,那么再整个树的节点集合中,只有固定的几种属性值,即text,id、iconCls和leaf(注意:这里只是例举了这几种常见的属性值,至于其他的属性值,还是留给大家去探究吧)。所以大多数网友问我,为什么自定义的属性值,在extjs4.0中使用record.get(\"xxxx\")为什么获取不到?这其中的原因就是在store中没有指定fields。也就是说,在本项目中,如果菜单树不指定fields,那么是无法往下进行开发的。
回到正题,要实现动态创建grid,我们先要可以让树有很多属性供我们所用,新建Menu.js,将这个js文件放置到app/model文件夹下。其代码为:
Ext.define('SMS.model.Menu', { extend: 'Ext.data.Model',
fields: ['id', 'text','iconCls','stores','columns'], root: {
expanded: true },
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp' } })
Ext.define('SMS.model.Menu', { extend: 'Ext.data.Model',
fields: ['id', 'text','iconCls','stores','columns'], root: {
expanded: true },
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp' } })
MenuLoader.asp返回如下json:
[{\"text\":\"备案列
\序',dataIndex:'ID'},{text:'公司名',dataIndex:'kehu_name'}],\"leaf\":true},{\"text\":\"新增备\[{\"text\":\"备案列
\序',dataIndex:'ID'},{text:'公司名',dataIndex:'kehu_name'}],\"leaf\":true},{\"text\":\"新增
备
表
号称案表号称案
\
修改app/store下的Menus.js,更改为如下代码:
Ext.define('SMS.store.Menus',{ extend: 'Ext.data.TreeStore', requires: 'SMS.model.Menu', model: 'SMS.model.Menu' })
Ext.define('SMS.store.Menus',{ extend: 'Ext.data.TreeStore', requires: 'SMS.model.Menu', model: 'SMS.model.Menu' })
新建bastore.js,将其放到app/store目录下,代码为:
Ext.define('SMS.store.bastore', { extend: 'Ext.data.Store',
requires: 'SMS.model.beianlistmodel', model: 'SMS.model.beianlistmodel' });
Ext.define('SMS.store.bastore', { extend: 'Ext.data.Store',
requires: 'SMS.model.beianlistmodel', model: 'SMS.model.beianlistmodel' });
新建beianlistmodel.js,将其放到app/model目录下,其代码为:
Ext.define('SMS.model.beianlistmodel', { extend: 'Ext.data.Model',
fields: ['ID', 'kehu_name']//,这里只列出了自增值id和客户名称。
//proxy: {
// type: 'ajax',
// url: '/server/getbeianlist.asp', // reader: {
// type: 'json', // root: 'results' // } // } });
Ext.define('SMS.model.beianlistmodel', { extend: 'Ext.data.Model',
fields: ['ID', 'kehu_name']//,这里只列出了自增值id和客户名称。
//proxy: {
// type: 'ajax',
// url: '/server/getbeianlist.asp', // reader: {
// type: 'json', // root: 'results' // } // } });
服务端代码没有编写,所以将获取数据的代码暂时注释掉。
接下来,我们修改app/controller目录下的Menu.js,增加一个stores属性,其代码为 stores: ['bastore'], 然后将测试代码
if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){ panel ={
title: 'New Tab ' + record.get('id'), iconCls: 'tabs',
html: 'Tab Body ' + record.get('id') + '
', closable: true }
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-panel\"); main.setActiveTab(panel); } }
if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
panel ={
title: 'New Tab ' + record.get('id'), iconCls: 'tabs',
html: 'Tab Body ' + record.get('id') + '
', closable: true }
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-panel\"); main.setActiveTab(panel); } }
全部删除,重新写如下代码:
if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
panel = Ext.create(\"Ext.grid.Panel\ store:record.get('stores'), columns:record.get('columns'), title: record.get('text'),
id:record.get('text')+record.get('id'), viewConfig: { stripeRows: true },
closable: true })
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-pane\"); main.setActiveTab(panel); }
}
if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
panel = Ext.create(\"Ext.grid.Panel\ store:record.get('stores'), columns:record.get('columns'), title: record.get('text'),
id:record.get('text')+record.get('id'), viewConfig: {
stripeRows: true },
closable: true })
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-pane\"); main.setActiveTab(panel); }
}
稍做说明:Ext.create(\"Ext.grid.Panel\"..... 首先创建了一个gridpanel,由于我们增加了菜单树的fields属性,那么我们可以获取所有fields指定的属性值,通过上面的json数据 {\"text\":\"备案列表\序号',dataIndex:'ID'},{text:'公司名称',dataIndex:'kehu_name'}],\"leaf\":true}
我们就实现了动态生成grid。app/controller目录下的Menu.js全部代码为:
Ext.define('SMS.controller.Menu',{ extend: 'Ext.app.Controller',
refs:[
{ref: 'smsmenu',selector: 'smstablepanel'},
{ref: 'tabPanel',selector:'smstablepanel'} ],
stores: ['bastore'], init:function(){ this.control({ 'smsmenu': {
itemmousedown: this.loadMenu } }) },
loadMenu:function(selModel, record){ if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
panel = Ext.create(\"Ext.grid.Panel\ store:record.get('stores'), columns:record.get('columns'), title: record.get('text'),
id:record.get('text')+record.get('id'),
viewConfig: { stripeRows: true },
closable: true })
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-pane\"); main.setActiveTab(panel); }
}
},
openTab : function (panel,id){
var o = (typeof panel == \"string\" ? panel : id || panel.id); var main = Ext.getCmp(\"content-pane\");
var tab = main.getComponent(o); if (tab) {
main.setActiveTab(tab);
} else if(typeof panel!=\"string\"){ panel.id = o;
var p = main.add(panel); main.setActiveTab(p); } } })
Ext.define('SMS.controller.Menu',{ extend: 'Ext.app.Controller', refs:[
{ref: 'smsmenu',selector: 'smstablepanel'},
{ref: 'tabPanel',selector:'smstablepanel'} ],
stores: ['bastore'], init:function(){ this.control({
'smsmenu': {
itemmousedown: this.loadMenu } }) },
loadMenu:function(selModel, record){ if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
panel = Ext.create(\"Ext.grid.Panel\ store:record.get('stores'), columns:record.get('columns'), title: record.get('text'),
id:record.get('text')+record.get('id'), viewConfig: { stripeRows: true },
closable: true })
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content-pane\"); main.setActiveTab(panel); }
}
},
openTab : function (panel,id){
var o = (typeof panel == \"string\" ? panel : id || panel.id); var main = Ext.getCmp(\"content-pane\"); var tab = main.getComponent(o); if (tab) {
main.setActiveTab(tab);
} else if(typeof panel!=\"string\"){ panel.id = o;
var p = main.add(panel); main.setActiveTab(p); } } })
声明,进行一个Extjs4.0MVC模式开发,是一个尝试的过程,随着Extjs4.x的不断升级、改进和修复,可能会造成当前项目无法运行或者报错,mhzg会持续关注extjs的最新动态,如果有大幅改动,mhzg.net会将最新的extjs4版本下载到本地进行测试,如果本项目无法运行,那么mhzg.net会进行修复并在mhzg.net上发布补充文档进行说明和修正。而在整个开发过程中,难免会有一些地方不尽人意,欢迎大家相互探讨、研究,共同进步,extjs4开发笔记将在第六章更名为 extjs4 MVC 开发笔记,请大家持续关注,谢谢!!
Extjs4开发笔记(六)——数据的增删改
(2011-12-29 20:43:16)
标签: 杂谈
转载
▼
分类: extjs4
我们介绍了动态Grid的显示,其地址是:Extjs4开发笔记(五)——动态grid,在上一章,
我们只做了数据的显示,并没有添加、删除和修改功能,本章内容,介绍如何进行添加、删除和修改。
一般的项目中,Gird功能不是很复杂的话,都会使用window来实现数据的添加、修改功能,而本实例中,由于使用了动态grid功能,这样就使得再使用动态window来实现数据的添加和修改就会变的非常困难。
幸好,Grid有RowEditing,下面,我们就用RowEditing来实现数据的添加和修改功能。在开始之前,我们先回顾下上一章的一些重点内容:
1、给Menu增加了field属性,使得我们在数据库中的一些字段可以被当做是Menu的节点集合中的对象来调用。
2、给菜单表增加了两个字段,分别是store和columns。在app/store/文件夹下,我们新建了bastore.js文件,那么再数据库中对应的字段中,填写内容为bastore,在columns字段中,我们添加了内容为{text:'序号',dataIndex:'ID'},{text:'公司名称',dataIndex:'kehu_name'}的数据。
最后,我们修改了Menu.js文件,使得Grid可以显示数据。 现在,我们修改columns中的数据为:
{text:'序号',dataIndex:'ID',width:50},{text:'公司名称',dataIndex:'kehu_name',width:260,editor:{allowBlank: false}},{text:'备案号',dataIndex:'beianhao',width:140,editor:{allowBlank: false}},{text:'备案密码',dataIndex:'beianpass',width:100,editor:{allowBlank: false}},{text:'备案邮箱',dataIndex:'beianemail',width:160,editor:{allowBlank: false}},{text:'备案邮箱密码',dataIndex:'emailpass',width:120,editor:{allowBlank: false}},{text:'备案账号',dataIndex:'beianzh',width:160,editor:{allowBlank: false}},{text:'账号密码',dataIndex:'beianzhpa',width:120,editor:{allowBlank: false}} {text:'序号',dataIndex:'ID',width:50},{text:'公司名称',dataIndex:'kehu_name',width:260,editor:{allowBlank: false}},{text:'备案号',dataIndex:'beianhao',width:140,editor:{allowBlank: false}},{text:'备案密码',dataIndex:'beianpass',width:100,editor:{allowBlank: false}},{text:'备案邮箱',dataIndex:'beianemail',width:160,editor:{allowBlank: false}},{text:'备案邮箱密码',dataIndex:'emailpass',width:120,editor:{allowBlank: false}},{text:'备案账号',dataIndex:'beianzh',width:160,editor:{allowBlank: false}},{text:'账号密码',dataIndex:'beianzhpa',width:120,editor:{allowBlank: false}} 在这些数据中,将所有字段都列了出来,并且制定了editor中allowBlank的数据位flase,就是说,这些内容必须填写。
接下来,我们需要修改app/controller下的menu.js文件,增加一些功能,使其可以添加、删除信息。修改内容如下:
if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
Ext.require(['Ext.grid.*']);
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing',{
clicksToMoveEditor: 1, autoCancel: true });
panel = Ext.create(\"Ext.grid.Panel\ store:record.get('stores'), columns:record.get('columns'), errorSummary:false,
title: record.get('text'),
id:record.get('text')+record.get('id'), columnLines: true, bodyPadding:0, closable: true,
bbar: Ext.create('Ext.PagingToolbar', { store: record.get('stores'), displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条', emptyMsg: \"没有数据\" }),
dockedItems: [{
xtype: 'toolbar', items: [{
text: '增加信息', iconCls: 'icon-add', handler: function(){
rowEditing.cancelEdit();
var panelStore = this.up(\"panel\").getStore(); var panelModel = this.up(\"panel\").getSelectionModel();
panelStore.insert(0,panelModel); rowEditing.startEdit(0, 0); } }, '-', {
itemId: 'delete', text: '删除信息',
iconCls: 'icon-delete', disabled: true,
handler: function(){ var selection = panel.getView().getSelectionModel().getSelection()[0];
var panelStore = this.up(\"panel\").getStore(); if (selection) {
panelStore.remove(selection); } }
}, '-' ,{
text:'刷新数据',
iconCls:'icon-refresh', handler:function(){
var panelStore = this.up(\"panel\").getStore(); var currPage = panelStore.currentPage; panelStore.removeAll(); panelStore.load(currPage); } }] }],
plugins: [rowEditing], listeners: {
'selectionchange': function(view, records) {
panel.down('#delete').setDisabled(!records.length); } } })
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content_panel\"); main.setActiveTab(panel); } }
if (record.get('leaf')) {
var panel = Ext.getCmp(record.get('id')); if(!panel){
Ext.require(['Ext.grid.*']);
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing',{ clicksToMoveEditor: 1, autoCancel: true });
panel = Ext.create(\"Ext.grid.Panel\ store:record.get('stores'), columns:record.get('columns'), errorSummary:false,
title: record.get('text'),
id:record.get('text')+record.get('id'), columnLines: true, bodyPadding:0, closable: true,
bbar: Ext.create('Ext.PagingToolbar', { store: record.get('stores'), displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条', emptyMsg: \"没有数据\" }),
dockedItems: [{ xtype: 'toolbar', items: [{
text: '增加信息', iconCls: 'icon-add', handler: function(){
rowEditing.cancelEdit();
var panelStore = this.up(\"panel\").getStore();
var panelModel = this.up(\"panel\").getSelectionModel(); panelStore.insert(0,panelModel); rowEditing.startEdit(0, 0); }
}, '-', {
itemId: 'delete', text: '删除信息',
iconCls: 'icon-delete', disabled: true,
handler: function(){
var selection = panel.getView().getSelectionModel().getSelection()[0]; var panelStore = this.up(\"panel\").getStore(); if (selection) {
panelStore.remove(selection); } }
}, '-' ,{
text:'刷新数据',
iconCls:'icon-refresh', handler:function(){
var panelStore = this.up(\"panel\").getStore(); var currPage = panelStore.currentPage; panelStore.removeAll(); panelStore.load(currPage); } }] }],
plugins: [rowEditing], listeners: {
'selectionchange': function(view, records) {
panel.down('#delete').setDisabled(!records.length); } }
})
this.openTab(panel,record.get('id')); }else{
var main = Ext.getCmp(\"content_panel\"); main.setActiveTab(panel); }
}
代码中,启用了插件rowEditing,增加了一个toolbar,上面添加了三个按钮,分别是添加信息,删除信息和刷新数据。添加信息的按钮,我们创建了一个handler,并且在其中获取了gird的Store和Model,并将其插入到grid的store中,删除信息的按钮中,设想这个按钮在没有选中任何行的时候,是不可用的,所以设置其disabled为true。还设置了handler,并且通过获取选择的行,将其从grid 的store中删除。并且,我们为grid增加了一个监听selectionchange,这样,我们就可以在选择中一条数据后,让删除信息的按钮变的可用。
目前,当点击添加按钮并添如信息后,只是存在于grid的store中,并没有将数据更新到数据库中,删除信息也是一样。接下来,需要修改app/store下的bastore.js,使其可以更新到数据中。 bastore.js:
Ext.define('SMS.store.bastore', { extend: 'Ext.data.Store',
requires: 'SMS.model.beianlistmodel', model: 'SMS.model.beianlistmodel', pageSize: 20, remoteSort: true, autoLoad:true, proxy: {
type: 'ajax',
url: '/server/getbeian.asp', reader: {
root: 'items',
totalProperty : 'total' },
simpleSortMode: true },
listeners:{
update:function(store,record){
var currPage = store.currentPage; //alert(record.get(\"ID\")) Ext.Ajax.request({
url:'/server/getbeian.asp?action=save', params:{
id : record.get(\"ID\"),
kehu_name:record.get(\"kehu_name\"), beianhao:record.get(\"beianhao\"), beianpass:record.get(\"beianpass\"), beianemail:record.get(\"beianemail\"), emailpass:record.get(\"emailpass\"), beianzh:record.get(\"beianzh\"), beianzhpa:record.get(\"beianzhpa\"), },
success:function(response){ store.removeAll(); store.load(currPage); } }); },
remove:function(store,record){
var currPage = store.currentPage; //alert(record.get(\"ID\")) Ext.Ajax.request({
url:'/server/getbeian.asp?action=del', params:{
id : record.get(\"ID\") },
success:function(response){ store.removeAll(); store.load(currPage); } }); } },
sorters: [{
property: 'ID', direction: 'DESC' }] });
Ext.define('SMS.store.bastore', { extend: 'Ext.data.Store',
requires: 'SMS.model.beianlistmodel', model: 'SMS.model.beianlistmodel', pageSize: 20, remoteSort: true, autoLoad:true, proxy: {
type: 'ajax',
url: '/server/getbeian.asp',
reader: {
root: 'items',
totalProperty : 'total' },
simpleSortMode: true },
listeners:{
update:function(store,record){ var currPage = store.currentPage; //alert(record.get(\"ID\")) Ext.Ajax.request({
url:'/server/getbeian.asp?action=save', params:{
id : record.get(\"ID\"),
kehu_name:record.get(\"kehu_name\"), beianhao:record.get(\"beianhao\"), beianpass:record.get(\"beianpass\"), beianemail:record.get(\"beianemail\"), emailpass:record.get(\"emailpass\"), beianzh:record.get(\"beianzh\"), beianzhpa:record.get(\"beianzhpa\"), },
success:function(response){ store.removeAll(); store.load(currPage); } }); },
remove:function(store,record){ var currPage = store.currentPage; //alert(record.get(\"ID\")) Ext.Ajax.request({
url:'/server/getbeian.asp?action=del', params:{
id : record.get(\"ID\") },
success:function(response){ store.removeAll(); store.load(currPage); } }); } },
sorters: [{
property: 'ID', direction: 'DESC' }] });
代码中,为store增加了两个监听,update和remove,并且将数据通过AJAX发送到服务端,在服务端进行处理,这里,只使用了update和remove。store中还有个add方法,此方法也是增加一条数据,按照常理来说。这个方法才是增加数据,但是我使用了add方法之后,点击添加信息,就会添加一条空数据,索性就使用update方法,将id值也发送到服务端,在服务端进行处理,服务端处理流程是:接收id值,判断id值是否为空,如果为空,则新增数据,如果不为空,则更新数据。
至此,一个grid的功能全部完成了。而且本项目所有的功能,都是如此,这样,只要再数据库中插入相应的行,在app/store和app/model中建立相关的js就可以了。至于其他功能,就不在此一一例举了。
声明一点,这个开发笔记实施到最后,有一些东西已经脱离了MVC的范畴,而且也没想到六章内容就结束了这个项目。从文章一到六,只是起一个抛砖引玉的作用。由于Extjs4有很大的变动,所以任何基于Extjs4.x MVC的项目,都是摸着石头过河,一点一点积累起来的,并不是说我的这些文章起到了指导性的作用,而是实际开发过程中的一些体会和经验。所有项目,有很多种方法可以实现需求。
最后,希望大家能通过阅读这些开发笔记,都有所提高、进步!下面列出所有开发笔记的链接。
Extjs4开发笔记(一)——准备工作 Extjs4开发笔记(二)——框架的搭建 关于Extjs4开发笔记(二)的补充说明 Extjs4开发笔记(三)——菜单的实现 Extjs4开发笔记(四)——实现登录功能 Extjs4开发笔记(五)——动态grid
Extjs4开发笔记(四)——实现登录功能
谈
(2011-12-29 20:47:41)
转载
▼
标签: 杂分类: extjs4
上篇文章介绍了如何实现菜单功能(点击查看),但是有个问题,就是管理系统必须是登录后才会显示菜单,而且菜单还要实现不同权限有不同的菜单项,本文将实现这个功能。
首先,将server/MenuLoader.asp修改,增加管理员验证功能。即 If Session(\"Manage\") <> \"\" Then '显示菜单项 End If
这时,重新打开页面,由于有了基本的管理员验证,菜单不显示了。
接下来,开始制作登录,在view文件夹下建立Login.js,checkcode.js,其中Login.js实现登录功能,有用户名、密码和验证码,验证码的实现,就是checkcode.js,由于篇幅问题,checkcode.js请查看本站另一篇文章,ExtJS4学习笔记(十)---ExtJS4图片验证码的实现。
主要是Login.js:
Ext.define(SMS.view.Login',{ extend:'Ext.window.Window', alias: 'widget.loginForm',
requires: ['Ext.form.*','SMS.view.CheckCode'], initComponent:function(){
var checkcode = Ext.create('SMS.view.CheckCode',{ cls : 'key',
fieldLabel : '验证码', name : 'CheckCode', id : 'CheckCode', allowBlank : false, isLoader:true,
blankText : '验证码不能为空',
codeUrl: '/include/checkCode.asp', width : 160 })
var form = Ext.widget('form',{ border: false, bodyPadding: 10, fieldDefaults: {
labelAlign: 'left', labelWidth: 55,
labelStyle: 'font-weight:bold' },
defaults: {
margins: '0 0 10 0' },
items:[{
xtype: 'textfield', fieldLabel: '用户名',
blankText : '用户名不能为空', name:'UserName', id:'UserName', allowBlank: false, width:240 },{
xtype: 'textfield', fieldLabel: '密 码', allowBlank: false,
blankText : '密码不能为空', name:'PassWord', id:'PassWord', width:240,
inputType : 'password' },checkcode], buttons:[{ text:'登录',
handler:function(){
var form = this.up('form').getForm(); var win = this.up('window'); if(form.isValid()){ form.submit({
clientValidation: true, waitMsg:'请稍后',
waitTitle:'正在验证登录', url:'/server/checklogin.asp', success: function(form, action) { //登录成功后。
//隐藏登录窗口,并重新加载菜单?? ?? ?? ??
win.hide();
Ext.getCmp('SystemMenus').store.load(); },
failure: function(form, action) { Ext.MessageBox.show({ width:150,
title:\"登录失败\
buttons: Ext.MessageBox.OK, msg:action.result.msg }) } }); } } }] })
Ext.apply(this,{ height: 160, width: 280,
title: '用户登陆', closeAction: 'hide', closable : false, iconCls: 'win', layout: 'fit', modal : true, plain : true, resizable: false, items:form });
this.callParent(arguments); } }); 最终效果:
修改controller目录下的Main.js:
Ext.define(SMS.controller.Main',{
extend: 'Ext.app.Controller', requires:['SMS.view.Login'], onLaunch : function(){ var win; if(!win){
win = Ext.create('SMS.view.Login').show(); } } })
这时,当页面加载的时候,会显示登录窗口,而登录成功后, 会隐藏登录窗口并加载菜单。最后附上登录成功后页面效果。
因篇幅问题不能全部显示,请点此查看更多更全内容