学习ExtJs4.0知识已有一个月的时间,结合模拟项目——资源池管理系统,谈一谈学习中碰到的问题,解决办法以及收获的经验,希望可以达到互相交流、学习、共成长的目的。
ExtJs4.0不同于3.0版本的最重要改变是MVC模式的引入。本文也以MVC模式为切入点,开始介绍我的ExtJs4.0之旅。
一、SSH框架搭建后台
模拟项目采用了SSH搭建后台,数据库采用Oracle9i,前台可通过action将封装好的json数据进行读取等操作,具体构建细节此文不介绍,测试用数据如下表:
PID ID TEXT LEAF PIDS
1 基本信息 FALSE 0
2 人员信息 TRUE 1
3 日常工作 FALSE 0
......
22 Grid示例 TRUE 21
23 权限管理 TRUE 5
24 角色管理 TRUE 5
25 test 测试页面 TRUE 21
Res.app.store.Tree数据
ID UNAME UPASSWORD XB XM AIH ADDSDATAS
123 admin admin 男 3 sfsf 2011-10-3
124 3 3 男 3 篮球, 足球 3 2011-10-6
Res.app.store.leaf.Test数据
二、建立文件结构
首先,按照API文档MVC模式介绍File Structure中的图示,建立模拟项目的文件结构,如图1所示。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZukXOsFTM2UTNwcTMyMTMfBzLclTMvwVMxETMwIzLcRnbl1GajFGd0F2LcRXZu5ibkN3YukGavw1LcpDc0RHaiojIsJye.gif)
三、页面代码
完成index.jsp代码的编写,主要完成页面编码格式定义,ExtJs4.0样式、库文件引入,引入该页面需要加载的js文件。
<!--页面编码定义,考虑到中文输出,一般采用utf-8编码格式。 -->
<%@ page language="java"pageEncoding="utf-8"%>
<!-- ExtJs4.0样式、类库导入。需要注意的是:ext-all.js(压缩后的Ext全部源码)和ext-all-debug.js(用于调试的无压缩Ext全部源码)只用导入一个即可。曾碰到在开发中若同时导入,在使用时出现异常的情况。-->
<link rel="stylesheet"type="text/css"href="<%=basePath%>ext4/resources/css/ext-all.css" target="_blank" rel="external nofollow" >
<script type="text/javascript"src="<%=basePath%>ext4/ext-all.js"></script>
<!--引入该页面需要加载的js文件。-->
<script type="text/javascript" src="app.js"></script>
四、Js部分代码
1.应用部分js代码(app.js)
该js代码段主要完成加载模式,命名空间,控制器的定义,组织起应用结构,通过Res.controller.Main控制器,利用别名将视图以“border”的布局方式渲染。
Ext.Loader.setConfig({enabled:true});//开启ExtJs4.0自动加载模式。
Ext.application({
name:'Res',//命名空间的名称,首字母大写,也可全部大写,不能与控制器重名。
appFolder:'app',//指定应用文件包名,与项目中一致,使用小写。
controllers:['Main'],//指定控制器,可以有多个,用’,’分隔,控制器名是controller目录下的js文件名。
launch:function(){
Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
{xtype:'header'},
{xtype:'menutree'},
{xtype:'south'},
{xtype:'tabpanel'}
]
});
}
});
2.控制器部分js代码(Res.controller.Main)
控制器继承自Ext.app.Controller基类,它将需要的视图、数据、模型导入,通过方法调用完成数据、模型、视图之间的交互控制。
Ext.define('Res.controller.Main',{
extend:'Ext.app.Controller',
views: [
'menu.Tree',
'menu.South',
'menu.TabPanel',
'menu.Header',
'leaf.Test'
],
stores:[
'Tree',
'leaf.Test'
],
models:['Test'],
refs:[
{ref:'menutree',selector:'treetab'},
{ref:'tabpanel',selector:'treetab'}
],
init:function(){
this.control({
'menutree':{
itemmousedown: this.loadMenu
}
})
},
loadMenu:function(selModel,record){
if(record.get('leaf')) {
var panel = Ext.getCmp(record.get('id'));
if(!panel){
panel ={
title:record.get('text'),
xtype:record.get('id'),
closable: true
}
this.openTab(panel,record.get('id'));
}else{
var main = Ext.getCmp("content");
main.setActiveTab(panel);
}
}
},
openTab : function (panel,id){
var o =(typeof panel == "string" ? panel : id || panel.id);
var main =Ext.getCmp("content");
var tab =main.getComponent(o);
if (tab) {
main.setActiveTab(tab);
} elseif(typeof panel!="string"){
panel.id = o;
var p = main.add(panel);
main.setActiveTab(p);
}
}
});
3.主页面js代码
视图部分要注意继承的基类一定是Ext的视图、组件,常用的有:Ext.Component、Ext.grid.Panel、Ext.tree.Panel、Ext.tab.Panel、Ext.window.Window等。视图如果定义了alias,可以经过控制器导入后,直接使用别名进行调用。调用时直接使用widget后面的字段。别名的部分也是ExtJs4.0的一个特色,为了便于理解可以这样去体会别名的机制:看作是通过代码自定义了一个视图,通过widget小控件包装起来,这样就可以让控制器识别并直接调用。当然Ext.require()、new等旧版本方法同样适用。
3.1.Res.view.Header(如图2)
Ext.define('Res.view.menu.Header', {
extend: 'Ext.Component',
alias:'widget.header',
initComponent: function() {
Ext.apply(this, {
xtype: 'box',
region: 'north',
html: '<h1>资源池管理系统</h1>',
height: 60
});
this.callParent(arguments);
}
});
3.2.Res.view.South(如图3)
Ext.define('Res.view.menu.South',{
extend: 'Ext.toolbar.Toolbar',
alias:'widget.south',
initComponent : function(){
Ext.apply(this,{
frame:true,
region:"south",
height:23,
items:[
"当前用户:Guest",
'->',
"技术支持:<ahref='http://www.chinasoft.com' target='_blank'style='text-decoration:none;'><font color='#0000FF'>http://www.chinasoft.com</font></a> "]
});
this.callParent(arguments);
}
});
3.3.Res.view.Tree(如图4)
Ext.define('Res.view.menu.Tree',{
extend:'Ext.tree.Panel',
alias:'widget.menutree',
initComponent:function(){
Ext.apply(this,{
title: '功能菜单',
margins : '1 1 1 1',
region:'west',
border : false,
split: true,
width : 212,
minSize : 130,
maxSize : 300,
containerScroll : true,
collapsible : true,
autoScroll: false,
useArrows:true,
frame:true,
store:'Tree'//在控制器中已导入,可直接通过文件名调用。
});
this.callParent(arguments);
}
});
3.4.Res.view.TabPanel(如图5)
Ext.define('Res.view.menu.TabPanel',{
extend: 'Ext.tab.Panel',
alias:'widget.tabpanel',
initComponent : function(){
Ext.apply(this,{
id:'content',
region: 'center',
margins : '1 1 1 1',
defaults: {
autoScroll:true,
bodyPadding: 10
},
activeTab: 0,
border: false,
plain: true,
items: [{
title: '首页',
layout: 'fit'
}]
});
this.callParent(arguments);
}
});
3.5.Res.view.leaf.Test(如图6)
Ext.define('Res.view.leaf.Test',{
extend:'Ext.grid.Panel',
alias:'widget.test',
store:'leaf.Test',
initComponent:function(){
Ext.apply(this,{
columnLines: true,
bodyPadding:0,
selModel:Ext.create('Ext.selection.CheckboxModel'),//加入选择框
dockedItems:[{
dock: 'bottom',
xtype: 'pagingtoolbar',//分页工具栏
store: 'leaf.Test',
pageSize: 5,
displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
emptyMsg: '没有数据'
}]//在grid底部显示分页信息
});
*****this.columns = [
Ext.create('Ext.grid.RowNumberer'),
{hidden:true,header:"id",dataIndex:"id"},
{header:"用户名",sortable:true,dataIndex:"uname"},
{header:"性别",dataIndex:"xb"},
{header:"姓名",dataIndex:"xm"},
{header:"爱好",dataIndex:"aih"},
{name:"datas",xtype:'datecolumn',format:'Y-m-d',header:'出生日期',dataIndex:"datas"},
{id:'adds',header:"住址",dataIndex:"adds"}
];//定义列信息、映射数据字段名、数据类型等。
this.callParent(arguments);
}
});
4.数据Store部分js代码
ExtJs4.0在数据的读取上做了比较大的修改,原有的异步Node、同步Node方式进行了封装,在4.0版本中有了新的proxy代理模块,减少了开发代码量,开发人员应注意检查数据路径的正确性,在读取封装好的json数据时,需要构造一个reader。
4.1.Res.store.Tree
Ext.define('Res.store.Tree',{
extend:'Ext.data.TreeStore',
autoload:true,
root:{
text:'功能模块',
expanded:true,
id:'0-1'
},
proxy:{
type:'ajax',
[url='tree_getTree_Select.action]url:'tree_getTree_Select.action'[/url]
}
});
4.2.Res.store.leaf.Test
Ext.define('Res.store.leaf.Test',{
extend:'Ext.data.Store',
model:'Res.model.Test',
autoLoad:true,
proxy:{
type:'ajax',
[url='user_jsonSelect.action]url:'user_jsonSelect.action'[/url],
actionMethods:'POST',
reader:{
type:'json',
root:'result',
totalProperty:'totalProperty',
successProperty:'success'
}
}
});
5.模型Model部分js代码(Res.model.Test)
模型层Model主要配合Store完成与后台实体类数据映射的名值配对,通过Store读取原始数据,在Model中形成与js代码相对应的fields数据。
Ext.define('Res.model.Test', {
extend : 'Ext.data.Model',
fields : [
{name : "id"},
{name : "uname"},
{name : "xb"},
{name : "xm"},
{name : "aih"},
{name : "datas",type:"date",mapping:'datas.time',dateFormat:"time"},
{name : "adds"}
]
});
五、总结
如图7所示,按照ExtJs4.0MVC模式,首先建立了文件结构,然后分别完成页面、应用层、视图层、数据层和模型层js代码的编写。页面加载应用层,应用层引入控制层和视图层,控制层通过各种方法完成视图、数据、模型层的交互控制,项目雏形已基本形成。
在项目进行过程中,遇到最多的问题是大小写、拼写异常,控制器引入异常,页面无显示且控制台无异常等。建议开发人员同时使用火狐(配合firebug)和IE浏览器,调试时,需要启用firebug网络功能,该功能可以反映出各种请求的响应情况,便于捕捉到产生问题的环节。控制台报错格式大多含义清晰,如碰到:g is null此类异常,请检查拼写。
本文只是学习ExtJs4.0的一个引子,还有很多未实现功能,项目进展到后期也有很多新问题,比如:js的不断增多会加重控制层负担,以至于影响加载速度;tabpanel中的不同tab会有重复代码,需要通过代码封装减少代码量;分页如何制作等等。希望有兴趣的朋友继续研究,多交流、讨论,由于个人技术所限,有很多不规范、可改进的地方,希望大家多提意见,共同进步。
不断地学习,每天让自己进步一点,从而站在不同的高度和角度,发现新的问题点,每一天都很新鲜,都很充实!