Extjs4学习
1 Ext js初步
1.1 获取Extjs
下载extjs:
可以从http://extjs.org.cn/ 获得需要的extjs发布包及更多支持。
1.2 搭建学习环境:
假设您的机器已经安装myeclipse和tomcat,如果没有请参阅其他相关资料。
myeclipse建立新Web project项目Extjs4
并且将extjs4.0.7压缩包解压后的全部文件复制到项目的Webroot目录下
Examples目录为ext官方提供的例子程序,其中可能包涵php的代码,错误信息可以暂时不理会。
部署并且启动tomcat,测试环境是否可用。
打开浏览器,输入http://localhost:8080/Ext4/index.html
假设您的tomcat端口为8080
您会看到以下界面,证明环境已经搭建成功!
查看api文档 http://localhost:8080/Ext4/docs/index.html
查看示例页面http://localhost:8080/Ext4/examples/index.html
1.3 测试例子
开始...
Webroot目录下建立hellowword.js 输入如下内容:
Ext.application({ name: 'HelloExt', launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: [ { title: 'Hello Ext', html : 'Hello! Welcome to Ext JS.' } ] }); } }); |
再建立一个helloword.html,输入如下内容
<html> <head> <title>Hello Ext</title> <link rel="stylesheet" type="text/css" href="resources/css/ext-all.css"> <script type="text/javascript" src="ext-all.js"></script> <script type="text/javascript" src="HelloExt.js"></script> </head> <body></body> </html> |
Html文件中只引入了一个css和2个js文件,注意引用路径和你建立文件路径是否能匹配,如果路径没有问题的话,打开浏览器输入
http://localhost:8080/Ext4/helloworld.html您将会看到浏览器里显示一个panel,标题是Hello Ext,内容Hello! Welcome to Ext JS.,如果没有,请查看是否有路径不匹配。
其他:
在ExtJS里最常用的,应该就是Ext.onReady和Ext.application这两个个方法了,而且它也可能是你学习ExtJS所接触的第一个方法,这个方法在当前的DOM加载完毕后自动调用,保证页面内的所有元素都能被Script所引用.可以尝试在这个方法中添加一条语句,看看页面打开后是什么反映
(先建立js和html文件,将如下代码加入js文件中,html文件相应引入对应的js文件, 本文档所有示例代码均如此方式运行以下不再重复)
Ext.onReady(function() { alert('hello world!'); }); |
上面的代码将在页面加载完毕后弹出一对话框,打印出'hello world!'字样.
获取元素
还有一个常用的方法,就是获取页面上的元素了,ExtJS提供了一个get方法,可以根据ID取到页面上的元素:
var myDiv = Ext.get('myDiv');
会取到页面上ID为'myDiv'的元素.如果使用Element.dom的方法,则可以直接操作底层的DOM节点,Ext.get返回的则是一个Element对象.
在不能使用这种方式来获取多个DOM的节点,或是要获取一些ID不一致,但又有相同特征的时候,可以通过选择器来进行获取,比如要获取页面上所有的
标签,则可以使用:
var ps = Ext.select('p');
这样你就可以对所要获取的元素进行操作了,select()方法返回的是Ext.CompositeElement对象,可以通过其中的each()方法对其所包含的节点进行遍历:
ps.each(function(el) { el.highlight(); }); |
当然,如果你要是对获取的所有元素进行相同的操作,可以直接应用于CompositeElement对象上,如:
ps.highlight();
或是:
Ext.select('p').highlight();
当然,select参数还可以更复杂一些,其中可以包括W3C Css3Dom选取器,基本的XPath,HTML属性等,详细情况,可以查看DomQuery API的文档,来了解细节.
事件响应
获取到了元素,则可能会对一些元素的事件进行一些处理,比如获取一个按钮,我们为它添加一个单击事件的响应:
复制代码 代码如下:
Ext.onReady(function() { Ext.get('myButton').on('click', function() { alert('You clicked the button!'); }); }); |
当然,你可以把事件的响应加到通过select()方法获取到的元素上:
复制代码 代码如下:
Ext.select('p').on('click', function() { alert('You clicked a paragraph!'); }); |
Widgets
ExtJS还提供了丰富的UI库来供大家使用.
2 Extjs4布局详解
2.1 Fit布局
在Fit布局中,子元素将自动填满整个父容器。注意:在fit布局下,对其子元素设置宽度是无效的。如果在fit布局中放置了多个组件,则只会显示第一个子元素
在Fit布局中,子元素将自动填满整个父容器。注意:在fit布局下,对其子元素设置宽度是无效的。如果在fit布局中放置了多个组件,则只会显示第一个子元素。典型的案例就是当客户要求一个window或panel中放置一个GRID组件,grid组件的大小会随着父容器的大小改变而改变。
示例代码:
Ext.application({ name: 'HelloExt', launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: [ { title: 'Hello Ext', html : 'Hello! Welcome to Ext JS.' } ] }); } }); |
2.2 Border布局
border布局:border布局也称边界布局,他将页面分隔为west,east,south,north,center这五个部分,我们需要在在其items中指定使用region参数为其子元素指定具体位置。
border布局:border布局也称边界布局,他将页面分隔为west,east,south,north,center这五个部分,我们需要在在其items中指定使用region参数为其子元素指定具体位置。
注意:north和south部分只能设置高度(height),west和east部分只能设置宽度(width)。north south west east区域变大,center区域就变小了。
参数 split:true 可以调整除了center四个区域的大小。
参数 collapsible:true 将激活折叠功能, title必须设置,因为折叠按钮是出现标题部分的。
center 区域是必须使用的,而且center 区域不允许折叠。Center区域会自动填充其他区域的剩余空间。尤其在Extjs4.0中,当指定布局为border时,没有指定center区域时,会出现报错信息。
示例代码:
Ext.application({ name:"HelloExt", launch:function () { Ext.create('Ext.panel.Panel', { width: 1024, height: 720, layout: 'border', items: [{ region: 'south', xtype: 'panel', height: 20, split: false, html: '欢迎登录!', margins: '0 5 5 5' },{ title: 'West Region is collapsible', region:'west', xtype: 'panel', margins: '5 0 0 5', width: 200, collapsible: true, id: 'west-region-container', layout: 'fit' },{ title: 'Center Region', region: 'center', xtype: 'panel', layout: 'fit', margins: '5 5 0 0', html:'在Extjs4中,center区域必须指定,否则会报错。' }], renderTo: Ext.getBody() }); } }); |
2.3 Accordion布局
accordion布局:accordion布局也称手风琴布局,在accordion布局下,在任何时间里,只有一个面板处于激活状态。其中每个面边都支持展开和折叠。
accordion布局:accordion布局也称手风琴布局,在accordion布局下,在任何时间里,只有一个面板处于激活状态。其中每个面边都支持展开和折叠。注意:只有Ext.Panels 和所有Ext.panel.Panel 子项,才可以使用accordion布局。
示例代码:
Ext.application({ name:"HelloExt", launch:function () { Ext.create('Ext.panel.Panel', { title: 'Accordion Layout', width: 300, height: 300, x:20, y:20, layout:'accordion', defaults: { bodyStyle: 'padding:15px' }, layoutConfig: { titleCollapse: false, animate: true, activeOnTop: true }, items: [{ title: 'Panel 1', html: 'Panel content!' },{ title: 'Panel 2', html: 'Panel content!' },{ title: 'Panel 3', html: 'Panel content!' }], renderTo: Ext.getBody() }); }}); |
2.4 Card布局
Card布局:这种布局用来管理多个子组件,并且在任何时刻只能显示一个子组件。这种布局最常用的情况是向导模式,也就是我们所说的分布提交。
Card布局:这种布局用来管理多个子组件,并且在任何时刻只能显示一个子组件。这种布局最常用的情况是向导模式,也就是我们所说的分布提交。Card布局可以使用layout:'card'来创建。注意:由于此布局本身不提供分步导航功能,所以需要用户自己开发该功能。由于只有一个面板处于显示状态,那么在初始时,我们可以使用setActiveItem功能来指定某一个面板的显示。当要显示下一个面板或者上一个面板的时候,我们可以使用getNext()或getPrev()来得到下一个或上一个面板。然后使用setDisabled方法来设置面板的显示。另外,如果面板中显示的是FORM布局,我们在点击下一个面板的时候,处理FORM中提交的元素,通过AJAX将表单中的内容保存到数据库中或者SESSION中。
下面的示例代码展示了一个基本的Card布局,布局中并没有包含form元素,具体情况,还要根据实际情况进行处理:
Ext.application({ name: 'HelloExt', launch: function() { var navigate = function(panel, direction){ var layout = panel.getLayout(); layout[direction](); Ext.getCmp('move-prev').setDisabled(!layout.getPrev()); Ext.getCmp('move-next').setDisabled(!layout.getNext()); }; Ext.create('Ext.panel.Panel', { title: 'Card布局示例', width: 300, height: 202, layout: 'card', activeItem: 0, x:30, y:60, bodyStyle: 'padding:15px', defaults: {border: false}, bbar: [{ id: 'move-prev', text: 'Back', handler: function(btn) { navigate(btn.up("panel"), "prev"); }, disabled: true }, '->', { id: 'move-next', text: 'Next', handler: function(btn) { navigate(btn.up("panel"), "next"); } }], items: [{ id: 'card-0', html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>' }, { id: 'card-1', html: '<p>Step 2 of 3</p>' }, { id: 'card-2', html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>' }], renderTo: Ext.getBody() }); } }); |
2.5 Anchor布局
anchor布局将使组件固定于父容器的某一个位置,使用anchor布局的子组件尺寸相对于容器的尺寸,即父容器容器的大小发生变化时,使用anchor布局的组件会根据规定的规则重新渲染位置和大小。
anchor布局将使组件固定于父容器的某一个位置,使用anchor布局的子组件尺寸相对于容器的尺寸,即父容器容器的大小发生变化时,使用anchor布局的组件会根据规定的规则重新渲染位置和大小。
AnchorLayout布局没有任何的直接配置选项(继承的除外),然而在使用AnchorLayout布局时,其子组件都有一个anchor属性,用来配置此子组件在父容器中所处的位置。
anchor属性为一组字符串,可以使用百分比或者是-数字来表示。配置字符串使用空格隔开,例如
anchor:'75% 25%',表示宽度为父容器的75%,高度为父容器的25%
anchor:'-300 -200',表示组件相对于父容器右边距为300,相对于父容器的底部位200
anchor:'-250 20%',混合模式,表示组件党对于如容器右边为250,高度为父容器的20%
示例代码:
Ext.application({ name: 'HelloExt', launch: function() { Ext.create('Ext.Panel', { width: 500, height: 400, title: "Anchor布局", layout: 'anchor', x:60, y:80, renderTo: Ext.getBody(), items: [{ xtype: 'panel', title: '75% Width and 25% Height', anchor: '75% 25%' },{ xtype: 'panel', title: 'Offset -300 Width & -200 Height', anchor: '-300 -200' },{ xtype: 'panel', title: 'Mixed Offset and Percent', anchor: '-250 30%' }] }); } }); |
2.6 Absolute布局
Absolute布局继承Ext.layout.container.Anchor 布局方式,并增加了X/Y配置选项对子组件进行定位,Absolute布局的目的是为了扩展布局的属性,使得布局更容易使用
Absolute布局继承Ext.layout.container.Anchor 布局方式,并增加了X/Y配置选项对子组件进行定位,Absolute布局的目的是为了扩展布局的属性,使得布局更容易使用。
Ext.application({ name:"HelloExt", launch:function () { Ext.create('Ext.form.Panel', { title: 'Absolute布局', width: 300, height: 275, x:20, y:90, layout:'absolute', defaultType: 'textfield', items: [{ x: 10, y: 10, xtype:'label', text: 'Send To:' },{ x: 80, y: 10, name: 'to', anchor:'90%' },{ x: 10, y: 40, xtype:'label', text: 'Subject:' },{ x: 80, y: 40, name: 'subject', anchor: '90%' },{ x:0, y: 80, xtype: 'textareafield', name: 'msg', anchor: '100% 100%' }], renderTo: Ext.getBody() }); } }); |
2.7 Column布局
Column布局一般被称为列布局,这种布局的目的是为了创建一个多列的格式。其中每列的宽度,可以为其指定一个百分比或者是一个固定的宽度。
Column布局一般被称为列布局,这种布局的目的是为了创建一个多列的格式。其中每列的宽度,可以为其指定一个百分比或者是一个固定的宽度。
Column布局没有直接的配置选项(继承的除外),但Column布局支持一个columnWidth属性,在布局过程中,使用columnWidth指定每个面板的宽度。
注意:使用Column布局布局时,其子面板的所有columnWidth值加起来必须介于0~1之间或者是所占百分比。他们的总和应该是1。
另外,如果任何子面板没有指定columnWidth值,那么它将占满剩余的空间。
示例代码:
Ext.application({ name:"HelloExt", launch:function () { Ext.create('Ext.panel.Panel', { title: 'Column Layout - 按比例', width: 350, height: 250, x:20, y:100, layout:'column', items: [{ title: 'Column 1', columnWidth: .25 },{ title: 'Column 2', columnWidth: .55 },{ title: 'Column 3', columnWidth: .20 }], renderTo: Ext.getBody() }); } }); |
3 Extjs4文档阅读
ExtJS4使用新的类机制进行了大量的重构。为了支撑新的架构,ext4几乎重写了每一个类,因此最好先好好的理解一下新的架构,再开始编码。
本文适合想在extjs4中扩展现有类或者创建新类的开发者。其实,不管是想扩展还是使用,都建议您仔细阅读一下(如果E文好的,建议您还是阅读英文原文。链接地址是:http://docs.sencha.com/ext-js/4-0/#/guide/ )。文章共分4个部分,建议每一部分都仔细研究下,对之后的开发工作,会有意想不到的好处。
3.1 系统类(class system)
Api文档路径:http://localhost/Ext4/docs/index.html#!/guide/class_system
第一部分:概述。说明了强大的类机制的必要性
第二部分:编码规范。讨论类、方法、属性、变量和文件命名
第三部分:DIY。详细的编码示例
第四部分:错误处理和调试。提供一些有用的调试和异常处理技巧
3.1.1 概述
ExtJS4拥有超过300个的类.迄今为止,我们的社区拥有超过20万来自世界各地,使用不同后台语言的开发者.要在这种规模的框架上提供具有以下特点的架构,需要面临巨大的挑战:
1、简单易学。
2,快速开发、调试简单、部署容易。
3,良好的结构、可扩展性和可维护性。
3.1.2 编码和规范
*在所有类、命名空间(namespace)和文件名中使用一致的命名约定,有助于保持代码的良好结构和可读性。
1) Classes
类名只能包含字母和数字。允许包含数字,但是大部分情况下不建议使用,除非这些数字是专业术语的一部分。不要使用下划线,连字符等非数字字母符号。例如:
MyCompany.useful_util.Debug_Toolbar is discouraged MyCompany.util.Base64 is acceptable |
类名应该包含在使用点号分隔的命名空间中。至少,要有一个顶级命名空间。例如:
MyCompany.data.CoolProxyMyCompany.Application |
顶级命名空间和实际的类名应使用驼峰命名(CamelCased),其他则为小写。例如:
MyCompany.form.action.AutoLoad |
不是Sencha开发的类(即不是Ext自带的)不要使用Ext做为顶级命名空间。缩写也要遵守以上的驼峰式命名约定。例如:
Ext.data.JsonProxy 代替 Ext.data.JSONProxy MyCompany.util.HtmlParser代替MyCompary.parser.HTMLParser MyCompany.server.Http代替MyCompany.server.HTTP |
2) 代码文件类名对应类所在的文件(包括文件名)。因此,每个文件应该只包含一个类(类名和文件名一样)。例如:
Ext.util.Observable 存放在 path/to/src/Ext/util/Observable.js
Ext.form.action.Submit 存放在 path/to/src/Ext/form/action/Submit.js
MyCompany.chart.axis.Numeric 存放在 path/to/src/MyCompany/chart/axis/Numeric.js
path/to/src 是你的应用所在目录。所有类都应该在这个通用根目录下,并且使用适当的命名空间以利于开发、维护和部署。
3)方法和变量
•和类命名一样,方法和变量也只能包含字母和数字。数字同样是允许但不建议,除非属于专业术语。不要使用下划线,连字符等任何非字母数字符号。
•方法和变量名一样使用驼峰式命名,缩写也一样。
•举例
•合适的方法名:
encodeUsingMd5() getHtml() 代替 getHTML()
getJsonResponse() 代替 getJSONResponse()
parseXmlContent() 代替parseXMLContent()
•合适的变量名:
var isGoodName
var base64Encoder
var xmlReader
var httpServer
4)属性
· 类属性名称遵循以上的变量和方法命名约定.除非是静态的常量.
· 类的静态属性常量应该全部大写。例如:
o Ext.MessageBox.YES = "Yes"
o Ext.MessageBox.NO = "No"
o MyCompany.alien.Math.PI = "4.13"
3.1.3 DIY亲自动手(示例代码)
3.1.3.1 声明
3.1.3.1.1 Extjs4之前的方式
如果你曾经使用过旧版本的extjs,那么你肯定熟悉使用Ext.extend来创建一个类:
1: var MyWindow=Ext.extend(Object,{...}); |
这个方法很容易从现有的类中继承创建新的类.相比直接继承,我们没有好用的API用于类创建的其他方面,诸如:配置、静态方法、混入(Mixins)。呆会我们再来详细的重新审视这些方面。现在,让我们来看看另一个例子:
1: My.cool.Window = Ext.extend(Ext.Window, { ... }); |
在这个例子中,我们创建我们的新类,继承Ext.Window,放在命名空间中。我们有两个问题要解决:
1,在我们访问My.cool的Window属性之前,My.cool必须是一个已有的对象.
2,Ext.Window必须在引用之前加载.
第一个问题通常使用Ext.namespace(别名Ext.ns)来解决.该方法递归创建(如果该对象不存在)这些对象依赖.比较繁琐枯燥的部分是你必须在Ext.extend之前执行Ext.ns来创建它们.
1: Ext.ns('My.cool'); 2: My.cool.Window = Ext.extend(Ext.Window, { ... }); |
第二个问题不好解决,因为Ext.Window可能直接或间接的依赖于许多其他的类,依赖的类可能还依赖其它类...出于这个原因,在ext4之前,我们通常引入整个ext-all.js,即使是我们只需要其中的一小部分.
3.1.3.1.2 Extjs4新的方式
在Extjs4中,你只需要使用一个方法就可以解决这些问题:Ext.define.以下是它的基本语法:
1: Ext.define(className, members, onClassCreated); |
className: 类名
members:代表类成员的对象字面量(键值对,json)
onClassCreated: 可选的回调函数,在所有依赖都加载完毕,并且类本身建立后触发.由于类创建的新的异步特性,这个回调函数在很多情况下都很有用.这些在第四节中将进一步讨论
例如:
· Ext.define('My.sample.Person', { name: 'Unknown', constructor: function(name) { if (name) { this.name = name; } return this; }, eat: function(foodType) { alert(this.name + " is eating: " + foodType); return this; } }); var aaron = Ext.create('My.sample.Person', 'Aaron'); aaron.eat("Salad"); |
程序执行结果会弹出alert窗口显示"Aaron is eating: Salad".
· 注意我们使用Ext.create()方法创建了My.sample.Person类的一个新实例.我们也可以使用新的关键字(new My.sample.Person())来创建.然而,建议养成始终用Ext.create来创建类示例的习惯,因为它允许你利用动态加载的优势.更多关于动态加载信息,请看入门指南:入门指南
3.1.3.2 配置
在ExtJS 4 ,我们引入了一个专门的配置属性,用于提供在类创建前的预处理功能.特性包括:
· 配置完全封装其他类成员
· getter和setter.如果类没有定义这些方法,在创建类时将自动生成配置的属性的getter和setter方法。
· 同样的,每个配置的属性自动生成一个apply方法.自动生成的setter方法内部在设置值之前调用apply方法.如果你要在设置值之前自定义自己的逻辑,那就重载apply方法.如果apply没有返回值,则setter不会设置值.看下面applyTitle的例子:
· Ext.define('My.own.Window', { /** @readonly */ isWindow: true, config: { title: 'Title Here', bottomBar: { enabled: true, height: 50, resizable: false } }, constructor: function(config) { this.initConfig(config); return this; }, applyTitle: function(title) { if (!Ext.isString(title) || title.length === 0) { alert('Error: Title must be a valid non-empty string'); } else { return title; } }, applyBottomBar: function(bottomBar) { if (bottomBar && bottomBar.enabled) { if (!this.bottomBar) { return Ext.create('My.own.WindowBottomBar', bottomBar); } else { this.bottomBar.setConfig(bottomBar); } } } }); |
以下是它的用法:
var myWindow = Ext.create('My.own.Window', { title: 'Hello World', bottomBar: { height: 60 } }); alert(myWindow.getTitle()); // alerts "Hello World" myWindow.setTitle('Something New'); alert(myWindow.getTitle()); // alerts "Something New" myWindow.setTitle(null); // alerts "Error: Title must be a valid non-empty string" myWindow.setBottomBar({ height: 100 }); // Bottom bar's height is changed to 100 |
3.1.3.3 Statics
静态成员可以使用statics配置项来定义
Ext.define('Computer', { statics: { instanceCount: 0, factory: function(brand) { // 'this' in static methods refer to the class itself return new this({brand: brand}); } }, config: { brand: null }, constructor: function(config) { this.initConfig(config); // the 'self' property of an instance refers to its class this.self.instanceCount ++; return this; } }); var dellComputer = Computer.factory('Dell'); var appleComputer = Computer.factory('Mac'); alert(appleComputer.getBrand()); // using the auto-generated getter to get the value of a config property. Alerts "Mac" alert(Computer.instanceCount); // Alerts "2" |
3.1.3.4 错误处理&调试
Extjs 4包含一些有用的特性用于调试和错误处理.你可以使用Ext.getDisplayName()来显示任意方法的名字.这对显示抛出异常的类和方法非常有用.
throw new Error('['+ Ext.getDisplayName(arguments.callee) +'] Some message here'); |
当使用Ext.define()定义的类中的方法抛出异常后.你将在调用堆栈中看到类和方法名(如果你使用webkit).例如,以下是chrome浏览器的效果:
javascript是一种类无关(原文:classless)、基于原型的语言。因此javascript最强大的特点是灵活。同样的事情可以使用不同的方式,不同的编码风格和技巧去完成。这个特点也会带来一些不可预测的风险。如果没有统一的编码规范,javascript代码将很难理解、维护和复用。
相反的,基于类的编程语言拥有较为流行的面向对象模型,强类型、内建的封装机制和强制的编码约束等特点。通过强制开发人员遵守一些大的原则来使代码的行为更容易被理解,以及提高可扩展性(这里不明白,javascript这类动态语言不是更容易扩展么?)和可伸缩性。但是,这类语言没有javascript的灵活性
3.2 MVC应用模式
一直想写一些Extjs4 MVC的东西,但由于自己的英文水平足够媲美小学5年纪的学生,所以在找了一些比我强一点的网友+机器翻译,总结出了以下这篇文章。但个人强烈建议去看英文原版(http://localhost/Ext4/docs/index.html#!/guide/application_architecture)。本段代码示例建议使用firefox firebug插件配合使用,ie无法加在console.log对象。
那么,我们开始吧!
对于Extjs来说,大客户端程序一直很难写,当你为大客户端程序添加更多的功能和项目的时候,项目的体积往往迅速增长。这样的大客户端程序很难组织和维持 ,所以,Extjs4配备了一个新的应用程序体系结构,它能结构化你的代码,那就是Extjs4 MVC。
Extjs4 MVC有别于其他MVC架构,Extjs有他自己定义:
1、Model是一个Field以及他的Data的集合,Modes知道如何通过Stores来表示数据,以能用于网格和其他组件。模型的工作很像Extjs3的记录集(Record class),通常通过数据加载器(Stores)渲染至网格(grid)和其他组件上边。
2、View:用以装载任何类型的组件—grid、tree和panel等等。
3、Controller—用来放使得app工作的代码,例如 render views , instantiating Models 或者其他应用逻辑。
本篇文章,我们将创建一个非常简单的应用程序,即用户数据管理,最后,你就会知道如何利用Extjs4 MVC去创建简单应用程序。Extjs4 MVC应用程序架构提供应用程序的结构性和一致性。这样的模式带来了一些重要的好处:
1、 每个应用程序的工作方式相同,我们只需要学习一次。
2、 应用程序之间的代码共享很容易,应为他们所有的工作方式都相同
3、 你可以使用EXTJS提供的构建工具创建你应用程序的优化版本。
既然是介绍Extjs4 MVC,那么,我们开始创建这个应用。
3.2.1 文件结构(File Structure):
Extjs4 MVC的应用程序和别的应用程序一样都遵循一个统一的目录结构。在MVC布局中,所有的类放在应用程序文件夹,它的子文件夹中包含您的命名空间,模型,视图,控制器和存储器。下面来通过简单的例子来看下怎样应用。
在这个例子中,我们将整个应用程序放到一个名为”account_manager”的文件夹下,”account_manager”文件夹中的结构如上图。现在编辑index.html,内容如下:
<html> <head> <title>Account Manager</title> <link rel="stylesheet" type="text/css" href="ext-4.0/resources/css/ext-all.css"> <script type="text/javascript" src="ext-4.0/ext-debug.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body></body> </html> |
3.2.2 创建app.js文件(Creating the application)
所有Extjs4 MVC应用从Ext.application的一个实例开始,应用程序中应包含全局设置、以及应用程序所使用的模型(model),视图(view)和控制器(controllers),一个应用程序还应该包含一个发射功能(launch function)。
现在来创建一个简单的账户管理应用。首先,需要选择一个命名空间(所有extjs4应用应该使用一个单一的全局变来来作为命名空间)。暂时,使用”AM”来作为命名空间。
Ext.application({ name: 'AM', appFolder: 'app', launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: [ { xtype: 'panel', title: 'Users', html : 'List of users will go here' } ] }); } }); |
Ext.application({
name: 'AM',
appFolder: 'app',
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [
{
xtype: 'panel',
title: 'Users',
html : 'List of users will go here'
}
]
});
}
});
以上代码,做了如下几件事。首先,调用Ext.application创建一个应用程序类的实例,设置了一个”AM”的命名空间,他将作为整个应用的全局变量,也将作为Ext.Loader的命名空间,然后通过appFolder来指定配置选项设置相应的路径。最后,创建了一个简单的发射功能,这里仅仅创建了一个Viewport,其中包含一个panel,使其充满整个窗口。
3.2.3 定义一个控制器(Defining a Controller)
控制器是整个应用程序的关键,他负责监听事件,并对某些时间做出相应的动作。现在我们创建一个控制器,将其命名为Users.js,其路径是app/controller/Users.js。然后,我们为Users.js添加如下代码:
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', init: function() { console.log('Initialized Users! This happens before the Application launch function is called'); } }); |
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
console.log('Initialized Users! This happens before the Application launch function is called');
}
});
完成之后,我们将创建好的控制器添加到程序配置文件:app.js中:
Ext.application({ ... controllers: [ 'Users' ], ... }); |
当我们访问index.html时,用户控制器(Users.js)自动加载,因为我们在上面的app.js中的定义中指定了。Init函数一个最棒的地方是控制器与视图的交互,这里的交互是指控制功能,因为他很容易就可以监听到视图类的事件处理函数,并采取相应的措施,并及时渲染相关信息到面板上来。
编写Users.js:
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', init: function() { this.control({ 'viewport > panel': { render: this.onPanelRendered } }); }, onPanelRendered: function() { console.log('The panel was rendered'); } }); |
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'viewport > panel': {
render: this.onPanelRendered
}
});
},
onPanelRendered: function() {
console.log('The panel was rendered');
}
});
在Users.js中,init函数使用this.control来负责监听,由于使用了新的ComponentQuery引擎,所以可以快速方便的找到页面上组件的引用(viewport > panel),这有些类似CSS选择器,通过匹配,快速的找到相匹配的组件。
在上面的init函数中,我们使用viewport > panel,来找到app.js中Viewport下的panel组件,然后,我们提供了一个对象的处理函数(this. onPanelRendered,注意,这里的对象是this,他的处理函数是onPanelRendered)。整个效果是,只要符合触发render事件的任何组件,那么onPanelRendered函数将被调用。当运行我们的应用程序,我们将看到以下内容。
3.2.4 定义一个视图(Defining a View)
到目前为止,应用程序只有几行,也只有两个文件,app.js和app/controller/Users.js。现在我们来增加一个grid,来显示整个系统中的所有用户。作为视图组件的一个子类,我们创建一个新的文件,并把他放到app/view/user目录下。命名为List.js。整个路径是这样的。app/view/user/List.js,下面,我们写List.js的代码:
Ext.define('AM.view.user.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.userlist', title : 'All Users', initComponent: function() { this.store = { fields: ['name', 'email'], data : [ {name: 'Ed', email: 'ed@sencha.com'}, {name: 'Tommy', email: 'tommy@sencha.com'} ] }; this.columns = [ {header: 'Name', dataIndex: 'name', flex: 1}, {header: 'Email', dataIndex: 'email', flex: 1} ]; this.callParent(arguments); } }); |
Ext.define('AM.view.user.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.userlist',
title : 'All Users',
initComponent: function() {
this.store = {
fields: ['name', 'email'],
data : [
{name: 'Ed', email: 'ed@sencha.com'},
{name: 'Tommy', email: 'tommy@sencha.com'}
]
};
this.columns = [
{header: 'Name', dataIndex: 'name', flex: 1},
{header: 'Email', dataIndex: 'email', flex: 1}
];
this.callParent(arguments);
}});
我们创建好的这个类,只是一个非常普通的类,并没有任何意义,为了能让我们更好的使用这个定义好的类,所以我们使用alias来定义一个别名,这个时候,我们的类可以使用Ext.create()和Ext.widget()创建,在其他组件的子组件中,也可以使用xtype来创建。
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', views: [ 'user.List' ], init: ... onPanelRendered: ... }); |
修改Users.js,增加views属性,修改app.js中的launch方法,将List渲染到Viewport。
Ext.application({ ... launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: { xtype: 'userlist' } }); } }); |
Ext.application({
... launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: {
xtype: 'userlist'
}
});
}});
看到这里,也许会有人开始抓狂了,这个user.List到底是怎么来的,为什么要这样写?如果你开始感到疑惑,那么不妨看看Ext.Loader是如何工作的(参见文档其他部分),在看过Ext.Loader之后,你就会明白了,User.List就是app/view/user下的List.js文件。为什么Ext要从view下找呢?因为我们在控制器中定了views: ['user.List']。这就是Extjs动态加载的强大之处,具体Ext.Loader,请看本站的其他文章,你就会明白了。当我们刷新页面。
3.2.5 控制网格(Controlling the grid)
要注意的是,onPanelRendered功能仍然是被调用的。这是因为gird匹配'viewport > panel'。
然后我们添加一个监听,当我们双击grid中的行,就可以编辑用户。
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', views: [ 'user.List' ], init: function() { this.control({ 'userlist': { itemdblclick: this.editUser } }); }, editUser: function(grid, record) { console.log('Double clicked on ' + record.get('name')); } }); |
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
views: ['user.List'],
init: function() {
this.control({
'userlist': {
itemdblclick: this.editUser
}
});
},
editUser: function(grid, record) {
console.log('Double clicked on ' +record.get('name'));
}
});
这里,我们修改了ComponentQuery的选择('userlist')和事件的名称('itemdblclick')和处理函数('editUser')。
如果要实现真正编辑用户,那么我们需要一个真正用于用户编辑window,接着,创建一个JS文件。其路径是:app/view/user/Edit.js,代码是这样的:
Ext.define('AM.view.user.Edit', { extend: 'Ext.window.Window', alias : 'widget.useredit', title : 'Edit User', layout: 'fit', autoShow: true, initComponent: function() { this.items = [ { xtype: 'form', items: [ { xtype: 'textfield', name : 'name', fieldLabel: 'Name' }, { xtype: 'textfield', name : 'email', fieldLabel: 'Email' } ] } ]; this.buttons = [ { text: 'Save', action: 'save' }, { text: 'Cancel', scope: this, handler: this.close } ]; this.callParent(arguments); } }); |
Ext.define('AM.view.user.Edit', {
extend: 'Ext.window.Window',
alias : 'widget.useredit',
title : 'Edit User',
layout: 'fit',
autoShow: true,
initComponent: function() {
this.items = [{
xtype: 'form',
items: [{
xtype: 'textfield',
name :'name',
fieldLabel: 'Name'
},
{
xtype: 'textfield',
name : 'email',
fieldLabel: 'Email'
}]
}];
this.buttons = [{
text: 'Save',
action: 'save'
},
{
text: 'Cancel',
scope: this,
handler: this.close
}];
this.callParent(arguments);
}
});
我们定义了一个子类,继承Ext.window.Window,然后使用initComponent创建了一个表单和两个按钮,表单中,两个字段分别装载用户名和电子邮件。接下来,我们修改视图控制器,使其可以载入用户数据。
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', views: [ 'user.List', 'user.Edit' ], init: ... editUser: function(grid, record) { var view = Ext.widget('useredit'); view.down('form').loadRecord(record); } }); |
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
views: [
'user.List',
'user.Edit'
],
init: ...
editUser: function(grid, record) {
var view = Ext.widget('useredit');
view.down('form').loadRecord(record);
}
});
首先,我们创建的视图,使用便捷的方法Ext.widget,这是相当于Ext.create('widget.useredit“)。然后我们利用ComponentQuery快速获取编辑用户的形式引用。在Ext JS4的每个组件有一个down函数,可以使用它快速的找到任何他的子组件。当双击Grid中的行,我们可以看到如下图所示:
3.2.6 创建Model 和 Store
|
|
Ext.define('AM.store.Users', { extend: 'Ext.data.Store', fields: ['name', 'email'], data: [ {name: 'Ed', email: 'ed@sencha.com'}, {name: 'Tommy', email: 'tommy@sencha.com'} ] }); |
Ext.define('AM.store.Users', {
extend: 'Ext.data.Store',
fields: ['name', 'email'],
data: [
{name: 'Ed', email: 'ed@sencha.com'},
{name: 'Tommy', email: 'tommy@sencha.com'}
]
});
接下来修改两个文件:
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', stores: [ 'Users' ], ... }); |
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
stores: [ 'Users' ],
...
});
修改app/view/user/List.js,使其引用Users
Ext.define('AM.view.user.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.userlist', //we no longer define the Users store in the `initComponent` method store: 'Users', ... }); |
Ext.define('AM.view.user.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.userlist',
//we no longer define the Users store in the `initComponent` method
store: 'Users',
...
});
现在,我们定义的用户控制器已经能顺利的加载数据了,到目前,我们所定义的Store已经足够使用了,但是Extjs4提供了一个强大的Ext.data.Model类,不如,我们利用它来重构下我们Store,创建app/model/User.js
Ext.define('AM.model.User', { extend: 'Ext.data.Model', fields: ['name', 'email'] }); |
Ext.define('AM.model.User', {
extend: 'Ext.data.Model',
fields: ['name', 'email']
});
创建好模型之后,我们将他引入用户控制:
//the Users controller will make sure that the User model is included on the page and available to our app Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', stores: ['Users'], models: ['User'], ... }); // we now reference the Model instead of defining fields inline Ext.define('AM.store.Users', { extend: 'Ext.data.Store', model: 'AM.model.User', data: [ {name: 'Ed', email: 'ed@sencha.com'}, {name: 'Tommy', email: 'tommy@sencha.com'} ] }); |
//the Users controller will make sure that the User model is included on the page and available to our app
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
stores: ['Users'],
models: ['User'],
...
});
// we now reference the Model instead of defining fields inline
Ext.define('AM.store.Users', {
extend: 'Ext.data.Store',
model: 'AM.model.User',
data: [
{name: 'Ed', email: 'ed@sencha.com'},
{name: 'Tommy', email: 'tommy@sencha.com'}
]
});
完成上面的代码后,刷新页面,看到的结果和以前的一样。
3.2.7 通过model保存数据
现在双击Grid中的行,会弹出编辑用户的window,实现Save来保存用户数据,我们需要修改init函数。
Ext.define('AM.controller.Users', { init: function() { this.control({ 'viewport > userlist': { itemdblclick: this.editUser }, 'useredit button[action=save]': { click: this.updateUser } }); }, updateUser: function(button) { console.log('clicked the Save button'); } }); |
Ext.define('AM.controller.Users', {
init: function() {
this.control({
'viewport > userlist': {
itemdblclick: this.editUser
},
'useredit button[action=save]': {
click: this.updateUser
}
});
},
updateUser: function(button) {
console.log('clicked the Save button');
}
});
在this.control中,我们增加了一个选择项,'useredit button[action=save]',当ComponentQuery找到符合的组件(button按钮,并且action动作为save),给他增加一个方法click,事件为updateUser。如图:
上图中,可以看到正确的Click事件,在updateUser函数中,需要一个正式的逻辑,来完成用户数据的更新。
updateUser: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); } |
updateUser: function(button) {
var win = button.up('window'),
form = win.down('form'),
record = form.getRecord(),
values =form.getValues();
record.set(values);
win.close();
}
这里,当我们点击Save按钮,我们将按钮本身传入函数updateUser,这时,我们使用button.up('window')来获取用户window的引用,然后使用win.down('form')来获取表单的引用,然后获取表单的记录,获取表单中的值,再设置记录为新的值,最后关闭window。
修改数据后点击Save,更新完成。
实际应用过程中,我们需要将数据保存到服务器,所以,需要修改文件,达到想要的目的。
3.2.8 将数据保存到服务器
这很容易做到。使用AJAX来实现即可。
Ext.define('AM.store.Users', { extend: 'Ext.data.Store', model: 'AM.model.User', autoLoad: true, proxy: { type: 'ajax', url: 'data/users.json', reader: { type: 'json', root: 'users', successProperty: 'success' } } }); |
Ext.define('AM.store.Users', {
extend: 'Ext.data.Store',
model: 'AM.model.User',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'data/users.json',
reader: {
type: 'json',
root: 'users',
successProperty: 'success'
}
}
});
在AM.store.Users,移除data,用一个代理(proxy)取代它,用代理的方式来加载和保存数据。在Extjs4中,代理的方式有AJAX, JSON-P 和 HTML5 localStorage,这里使用AJAX代理。数据从data/users.json中得到。
我们还用reader来解析数据,还指定了successProperty配置。具体请查看Json Reader,把以前的数据复制到users.json中。得到如下形式:
{ success: true, users: [ {id: 1, name: 'Ed', email: 'ed@sencha.com'}, {id: 2, name: 'Tommy', email: 'tommy@sencha.com'} ] } |
{ success: true, users: [
{id: 1, name: 'Ed', email: 'ed@sencha.com'},
{id: 2, name: 'Tommy', email: 'tommy@sencha.com'}
]}
到这里,唯一的变化是将Stroe的autoLoad设置为了true,这要求Stroe的代理要自动加载数据,当刷新页面,将得到和之前一样的效果。
最后一件事情,是将修改的数据发送到服务器,对于本例,服务端只用了一个静态的JSON,所以我们不会看到有任何变化,但至少可以确定这样做是可行的,相信服务端处理数据能力,大家都应该有。本例用,做个小小的变化,即新的代理中,使用api来更新一个新的URL。
proxy: { type: 'ajax', api: { read: 'data/users.json', update: 'data/updateUsers.json' }, reader: { type: 'json', root: 'users', successProperty: 'success' } } |
proxy: {
type: 'ajax',
api: {
read: 'data/users.json',
update: 'data/updateUsers.json'
},
reader: {
type: 'json',
root: 'users',
successProperty: 'success'
}
}
再来看看是如何运行的,我们还在读取users.json中的数据,而任何更新都将发送到updateUsers.json,在updateUsers.json中,创建一个虚拟的回应,从而让我们知道事件确实已经发生。updateUsers.json只包含了{"success": true}。而我们唯一要做的事情是服务的同步编辑。在updateUser函数增加这样一个功能。
updateUser: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); this.getUsersStore().sync(); } |
updateUser: function(button) {
var win = button.up('window'),
form = win.down('form'),
record = form.getRecord(),
values = form.getValues();
record.set(values);
win.close();
this.getUsersStore().sync();
}
现在,运行这个完整的例子,当双击grid中的某一样并进行编辑,点击Save按钮后,得到正确的请求和回应。至此,整个MVC模式全部完成,下一节,将讨论控制器(Controller)和模式(patterns),而这些,可以使应用代码更少,更容易维护
3.3 布局和容器
extjs4.0布局和容器(Layouts and Containers)是Ext JS中最强大的部分之一。它负责控制你应用程序中每个组件的尺寸和定位。本文内容包括了如何运用布局的基础。
3.3.1 容器
一个Ext JS应用程序的图形用户界面(UI)是由许多组件(查看组件指南(Components Guide)获取更多关于组件的信息)构成的。容器是一种可以容纳一些其他组件的特殊类型组件。
典型的Ext JS应用程序是由一些嵌套的组件构成不同的层来组成的。如下图:
最通用的容器就是Panel。让我们看下如何创建一个容器以允许一个Panel包含其他的组件:
Ext.create('Ext.panel.Panel', { renderTo: Ext.getBody(), width: 400, height: 300, title: 'Container Panel', items: [ { xtype: 'panel', title: 'Child Panel 1', height: 100, width: '75%' }, { xtype: 'panel', title: 'Child Panel 2', height: 100, width: '75%' } ] }); |
Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
width: 400,
height: 300,
title: 'Container Panel',
items: [
{
xtype: 'panel',
title: 'Child Panel 1',
height: 100,
width: '75%'
},
{
xtype: 'panel',
title: 'Child Panel 2',
height: 100,
width: '75%'
}
]
});
我们刚刚创建了一个Panel,并把它渲染到DOM的body中,然后我们使用items配置项添加两个子Panel到我们的Panel容器中。
3.3.2 布局
每个容器都拥有一个布局来管理它子组件的尺寸和定位。
下面我们将讨论如何使用指定类型的布局来配置容器,以及布局系统是如何使每个组件都保持协调的。
使用布局:上面例子中我们没有为Panel容器指定布局。可以看到子Panel是一个接着一个被放置的,正如DOM中标准的块元素一样。
之所以这样是因为所有容器的默认布局为自动布局。自动布局并不为子元素指定任何特殊的定位和尺寸规则。
例如,我们假定想让两个子Panel并排放置,并且每个占据父容器宽度的50%,我们可以简单
地使用一个列布局(Column Layout),通过在父容器中配置layout选项来实现。
代码:
1. Ext.create('Ext.panel.Panel', {
2. renderTo: Ext.getBody(),
3. width: 400,
4. height: 200,
5. title: 'Container Panel',
6. layout: 'column',
7. items: [
8. {
9. xtype: 'panel',
10. title: 'Child Panel 1',
11. height: 100,
12. width: '50%'
13. },
14. {
15. xtype: 'panel',
16. title: 'Child Panel 2',
17. height: 100,
18. width: '50%'
19. }
20. ]
21. });
Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
width: 400,
height: 200,
title: 'Container Panel',
layout: 'column',
items: [
{
xtype: 'panel',
title: 'Child Panel 1',
height: 100,
width: '50%'
},
{
xtype: 'panel',
title: 'Child Panel 2',
height: 100,
width: '50%'
}
]
});
Ext JS拥有一整套可用的布局,几乎容纳了你能想象到的任何一种布局。可以查看布局的例子(Layout Examples)以了解那些布局是可行的。
布局系统是如何工作的呢?
父容器的布局负责其所有子元素的初始定位和尺寸大小。框架内部调用了容器的doLayout方法,
该方法触发布局为父容器的所有子元素计算得到正确的尺寸和定位并更新DOM。doLayout方法
是递归调用的,所以父容器的任何子元素同样也将会调用它们的doLayout方法。这种调用将持续到到达组件层次的末端。通常你一般不会在你的应用程序代码中调用doLayout方法,因为框架已为你调用了。
父容器的改变尺寸时,或当添加或删除子组件的items时,重新布局将被触发。通常,我们仅依赖框架为我们管理以更新布局,但是有时我们想阻止框架自动更新布局,这样我们一次可以批量
地处理多个操作,完成时,我们手动地触发布局。
为了到达这个目的,我们在容器中使用延缓布局(suspendLayout)标志以阻止它自动布局,当我们执行那些通常会触发布局的操作时(例如添加或移除items)。
当我们做完这些操作时,我们必须把延缓布局标志关闭,并且手动地调用容器的doLayout方法以触发布局:
1. var containerPanel = Ext.create('Ext.panel.Panel', {
2. renderTo: Ext.getBody(),
3. width: 400,
4. height: 200,
5. title: 'Container Panel',
6. layout: 'column',
7. suspendLayout: true // Suspend automatic layouts while we do several different things that could trigger a layout on their own
8. //当我们做一些能触发其自动布局的操作时,保证延缓其能自动布局。
9. });
10. // Add a couple of child items. We could add these both at the same time by passing an array to add(),
11. //添加一对子items。我们可以通过调用add()并传递一个数组一次性添加这两个子组件,
12. // but lets pretend we needed to add them separately for some reason.
13. //但是我们假定需要分别地添加它们是因为其他原因。
14. containerPanel.add({
15. xtype: 'panel',
16. title: 'Child Panel 1',
17. height: 100,
18. width: '50%'
19. });
20. containerPanel.add({
21. xtype: 'panel',
22. title: 'Child Panel 2',
23. height: 100,
24. width: '50%'
25. });
26. // Turn the suspendLayout flag off.
27. //关闭延缓布局标志
28. containerPanel.suspendLayout = false;
29. // Trigger a layout.
30. //触发布局
31. containerPanel.doLayout();
var containerPanel = Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
width: 400,
height: 200,
title: 'Container Panel',
layout: 'column',
suspendLayout: true // Suspend automatic layouts while we do several different things that could trigger a layout on their own
//当我们做一些能触发其自动布局的操作时,保证延缓其能自动布局。
});
// Add a couple of child items. We could add these both at the same time by passing an array to add(),
//添加一对子items。我们可以通过调用add()并传递一个数组一次性添加这两个子组件,
// but lets pretend we needed to add them separately for some reason.
//但是我们假定需要分别地添加它们是因为其他原因。
containerPanel.add({
xtype: 'panel',
title: 'Child Panel 1',
height: 100,
width: '50%'
});
containerPanel.add({
xtype: 'panel',
title: 'Child Panel 2',
height: 100,
width: '50%'
});
// Turn the suspendLayout flag off.
//关闭延缓布局标志
containerPanel.suspendLayout = false;
// Trigger a layout.
//触发布局
containerPanel.doLayout();
3.3.3 组件布局
如容器的布局定义了一个容器如何设定尺寸和定位它的组件items,组件的布局同样也定义了
其如何设定尺寸和定位它内部的子items。
组件的布局通过使用组件布局(componentLayout)来设置配置选项。一般你不需要使用该配置项,除非你正在编写一个自定义的组件,因为所有提供的组件其内部元素的大小调整和定位都拥有它们自己的布局管理器。
大多数组件使用自动布局(Auto Layout),但是更多复杂的组件需要自定义的组件布局方式(例如一个有header,footer,toolbars的Panel)
自此,我们了解了容器及布局,并且掌握了基本的布局方式和容器的布局方法。下章,我们将详细介绍组件的一些使用方法。
3.4 数据
上一节,我们介绍了下布局及容器,本节本来要介绍下组件,但是组件方面的资料暂时没有翻译完成,所以本节先介绍下数据(Data)。
数据
Data包负责加载和保存你应用程序中的所有数据,由41个类构成,其中有三个类是最重要的,分别是模型类(Model),存储类(Store),代理类(Ext.data.proxy.Proxy)。它们几乎在每个应用程序中都被使用到,并且有很多相附类为它们提供支持。
3.4.1 模型类和存储类
模型类(Ext.data.Model)是data包的核心部件。每个模型代表应用程序中的一些数据类型-例如一个电子商务应用程序可以有Users、Products、Orders等模型类。简单来说,模型仅仅是一些域和每个域对应数据的集合。我们将重点研究模型类中的四个主要部分—域(Fields)、代理(Proxies)、关联(Associations)和验证(Validations)。
现在让我们看看如何创建一个模型类:
Ext.define('User', { extend: 'Ext.data.Model', fields: [ { name: 'id', type: 'int' }, { name: 'name', type: 'string' } ] }); |
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' }
]
});
模型类通常在存储类中使用,这些存储类主要是一些模型实例的集合。设置存储类和加载它的数据是很简单的:
Ext.create('Ext.data.Store', { model: 'User', proxy: { type: 'ajax', url : 'users.json', reader: 'json' }, autoLoad: true }); |
Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url : 'users.json',
reader: 'json'
},
autoLoad: true
});
我们用Ajax代理( Ajax Proxy)配置我们的存储类,告诉它加载数据的url地址和用来解码数据的读取器(Reader)。这样,我们的服务器将返回JSON格式的数据,我们可以使用设置的Json读取器(Json Reader)来解析响应。上面创建的存储类实例从url地址users.json中自动加载一系列User模型类实例的数据。
users.json应该返回如下所示的JSON字符串:
1. {
2. success: true,
3. users: [
4. { id: 1, name: 'Ed' },
5. { id: 2, name: 'Tommy' }
6. ]
7. }
8.
{
success: true,
users: [
{ id: 1, name: 'Ed' },
{ id: 2, name: 'Tommy' }
]
}
请查看Simple Store获取一个演示实例。
3.4.2 内联数据
存储类实例也可以加载内联数据,它们转换每个传递进data中的对象为模型类实例:
1. Ext.create('Ext.data.Store', {
2. model: 'User',
3. data: [
4. { firstName: 'Ed', lastName: 'Spencer' },
5. { firstName: 'Tommy', lastName: 'Maintz' },
6. { firstName: 'Aaron', lastName: 'Conran' },
7. { firstName: 'Jamie', lastName: 'Avins' }
8. ]
9. });
10.
Ext.create('Ext.data.Store', {
model: 'User',
data: [
{ firstName: 'Ed', lastName: 'Spencer' },
{ firstName: 'Tommy', lastName: 'Maintz' },
{ firstName: 'Aaron', lastName: 'Conran' },
{ firstName: 'Jamie', lastName: 'Avins' }
]
});
内联数据的例子(Inline Data example)
排序和分组
存储类实例能在本地执行排序、过滤和分组,同样也提供远程排序、过滤和分组:
1. Ext.create('Ext.data.Store', {
2. model: 'User',
3. sorters: ['name', 'id'],
4. filters: {
5. property: 'name',
6. value : 'Ed'
7. },
8. groupField: 'age',
9. groupDir: 'DESC'
10. });
11.
Ext.create('Ext.data.Store', {
model: 'User',
sorters: ['name', 'id'],
filters: {
property: 'name',
value : 'Ed'
},
groupField: 'age',
groupDir: 'DESC'
});
我们刚刚创建的存储类实例中,数据首先将按照name排序,其次按id排序;并且数据将被过滤为仅包含name为’Ed’的Users,然后数据将按年龄进行分组且遵循由小到大的顺序。任何时候调用存储类的API进行排序、过滤和分组都是是很轻松的。查看排序、分组、过滤存储类实例(Sorting Grouping Filtering Store)获取一个演示示例。
3.4.3 代理
代理类被存储类使用以便于管理加载和保存模型类数据。有两种类型的代理:客户端代理(Client)和服务器端代理(Server)。客户端代理包括存储数据在浏览器内存的内存方式(Memory)和使用HTML5本地存储器(可用时)的本地存储方式(Local Storage)。服务器端代理操作一些从远程服务器调度来的数据,例如包括Ajax,Jsonp和Rest方式。
代理方式可以直接在模型类中定义,如下:
1. Ext.define('User', {
2. extend: 'Ext.data.Model',
3. fields: ['id', 'name', 'age', 'gender'],
4. proxy: {
5. type: 'rest',
6. url : 'data/users',
7. reader: {
8. type: 'json',
9. root: 'users'
10. }
11. }
12. });
13.
14.
15.
16. // Uses the User Model's Proxy
17. Ext.create('Ext.data.Store', {
18. model: 'User'
19. });
20.
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'age', 'gender'],
proxy: {
type: 'rest',
url : 'data/users',
reader: {
type: 'json',
root: 'users'
}
}
});
// Uses the User Model's Proxy
Ext.create('Ext.data.Store', {
model: 'User'
});
这对我们有两方面的好处:首先,使得每个使用User模型类的存储类实例以相同方式加载数据变得可行,这样我们避免了必须为每个存储类实例复制相同代理方式的定义。其次,现在我们可以不必使用存储类来加载和保存模型数据:
1. // Gives us a reference to the User class
2. // 创建一个User类的引用
3. var User = Ext.ModelMgr.getModel('User');
4. var ed = Ext.create('User', {
5. name: 'Ed Spencer',
6. age : 25
7. });
8.
9. // We can save Ed directly without having to add him to a Store first because we
10. //我们可以直接保存ed而不用先把它添加到一个存储类中,因为我们配置了
11. // configured a RestProxy this will automatically send a POST request to the url /users
12. //一个能自动发送一个POST请求到指定url的Rest代理
13. ed.save({
14. success: function(ed) {
15. console.log("Saved Ed! His ID is "+ ed.getId());
16. }
17. });
18.
19.
20. // Load User 1 and do something with it (performs a GET request to /users/1)
21. //加载User 1并对其一些相关操作(执行一个GET请求到 /users/1)
22. User.load(1, {
23. success: function(user) {
24. console.log("Loaded user 1: " + user.get('name'));
25. }
26. });
27.
// Gives us a reference to the User class
// 创建一个User类的引用
var User = Ext.ModelMgr.getModel('User');
var ed = Ext.create('User', {
name: 'Ed Spencer',
age : 25
});
// We can save Ed directly without having to add him to a Store first because we
//我们可以直接保存ed而不用先把它添加到一个存储类中,因为我们配置了
// configured a RestProxy this will automatically send a POST request to the url /users
//一个能自动发送一个POST请求到指定url的Rest代理
ed.save({
success: function(ed) {
console.log("Saved Ed! His ID is "+ ed.getId());
}
});
// Load User 1 and do something with it (performs a GET request to /users/1)
//加载User 1并对其一些相关操作(执行一个GET请求到 /users/1)
User.load(1, {
success: function(user) {
console.log("Loaded user 1: " + user.get('name'));
}
});
也有利用HTML5新功能-- LocalStorage和 SessionStorage – 的代理模式。尽管旧的浏览器不支持这些新的HTML5 APIs,它们仍然是有用的,因为很多应用程序将从这些新特性的存在中受益。
直接在模型类中使用代理的例子(Example of a Model that uses a Proxy directly)
3.4.4 关联
模型类之间可以通过关联API链接在一起。大多数应用程序需要处理很多不同的模型类,并且这些模型类之间通常是相关联的。例如一个博客写作应用程序可能有User(用户)、Post(文章)、Comment(评论)等模型类。每个用户(User)可以创建多篇文章(Posts),并且每篇文章接受很多评论(Comments)。我们可以如下表示这些关系:
1. Ext.define('User', {
2. extend: 'Ext.data.Model',
3. fields: ['id', 'name'],
4. proxy: {
5. type: 'rest',
6. url : 'data/users',
7. reader: {
8. type: 'json',
9. root: 'users'
10. }
11. },
12. hasMany: 'Post' // shorthand for { model: 'Post', name: 'posts' }
13. });
14.
15.
16. Ext.define('Post', {
17. extend: 'Ext.data.Model',
18. fields: ['id', 'user_id', 'title', 'body'],
19. proxy: {
20. type: 'rest',
21. url : 'data/posts',
22. reader: {
23. type: 'json',
24. root: 'posts'
25. }
26. },
27. belongsTo: 'User',
28. hasMany: { model: 'Comment', name: 'comments' }
29. });
30.
31.
32. Ext.define('Comment', {
33. extend: 'Ext.data.Model',
34. fields: ['id', 'post_id', 'name', 'message'],
35. belongsTo: 'Post'
36. });
37.
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name'],
proxy: {
type: 'rest',
url : 'data/users',
reader: {
type: 'json',
root: 'users'
}
},
hasMany: 'Post' // shorthand for { model: 'Post', name: 'posts' }
});
Ext.define('Post', {
extend: 'Ext.data.Model',
fields: ['id', 'user_id', 'title', 'body'],
proxy: {
type: 'rest',
url : 'data/posts',
reader: {
type: 'json',
root: 'posts'
}
},
belongsTo: 'User',
hasMany: { model: 'Comment', name: 'comments' }
});
Ext.define('Comment', {
extend: 'Ext.data.Model',
fields: ['id', 'post_id', 'name', 'message'],
belongsTo: 'Post'
});
这将使得在你的应用程序中表示这种复杂关系变得简单。 每个模型类可以和其他模型类有任意多的关联,并且你的模型类可以按任意顺序定义。一旦我们创建了一个模型类实例,我们可以很轻松地遍历与其相关联的数据 -- 例如,如果我们想记录一个给定用户的每篇文章的所有相关评论,我们可以如下这样操作:
1. // Loads User with ID 1 and related posts and comments using User's Proxy
2. //加载User使用ID 1和相关的文章和评论使用User的代理
3. User.load(1, {
4. success: function(user) {
5. console.log("User: " + user.get('name'));
6. user.posts().each(function(post) {
7. console.log("Comments for post: " + post.get('title'));
8. post.comments().each(function(comment) {
9. console.log(comment.get('message'));
10. });
11. });
12. }
13. });
14.
// Loads User with ID 1 and related posts and comments using User's Proxy
//加载User使用ID 1和相关的文章和评论使用User的代理
User.load(1, {
success: function(user) {
console.log("User: " + user.get('name'));
user.posts().each(function(post) {
console.log("Comments for post: " + post.get('title'));
post.comments().each(function(comment) {
console.log(comment.get('message'));
});
});
}
});
上例我们创建每一个的hasMany关联将产生一个新方法添加到这个模型类上。我们声明的每个User模型类实例有许多(hasMany)文章(Posts),这将为我们添加user.posts()方法,如上面代码段中使用的那样。调用user.posts()方法将返回一个配置了Post模型的存储类实例。依次类推,Post模型实例获取了一个comments()方法,因为我们为其设置了hasMany 评论关联。关联不仅对加载数据来说是有用的,而且对创建新记录也是有用的:
1. user.posts().add({
2. title: 'Ext JS 4.0 MVC Architecture',
3. body: 'It\'s a great Idea to structure your Ext JS Applications using the built in MVC Architecture...'
4. });
5. user.posts().sync();
6.
user.posts().add({
title: 'Ext JS 4.0 MVC Architecture',
body: 'It\'s a great Idea to structure your Ext JS Applications using the built in MVC Architecture...'
});
user.posts().sync();
这里我们实例化了一个新的Post模型类,该实例将自动把User中的id赋值给Post中的user_id字段。调用sync()方法,将通过配置的代理方式来保存新创建的Post模型类实例 – 再者,如果你想让操作完成时得到反馈,你可以调用异步操作并传递进一个回调函数来实现。属于(belongsTo)关联也能在模型类实例中生成一个新方法,如我们下面介绍的这个示例:
1. // get the user reference from the post's belongsTo association
2. //得到user实例引用从post实例的belongsTo关联配置
3. post.getUser(function(user) {
4. console.log('Just got the user reference from the post: ' + user.get('name'))
5. });
6.
7. // try to change the post's user
8. //尝试改变文章的user
9. post.setUser(100, {
10. callback: function(product, operation) {
11. if (operation.wasSuccessful()) {
12. console.log('Post\'s user was updated');
13. } else {
14. console.log('Post\'s user could not be updated');
15. }
16. }
17. });
18.
// get the user reference from the post's belongsTo association
//得到user实例引用从post实例的belongsTo关联配置
post.getUser(function(user) {
console.log('Just got the user reference from the post: ' + user.get('name'))
});
// try to change the post's user
//尝试改变文章的user
post.setUser(100, {
callback: function(product, operation) {
if (operation.wasSuccessful()) {
console.log('Post\'s user was updated');
} else {
console.log('Post\'s user could not be updated');
}
}
});
再次说明,加载函数(getUser)是异步调用的,并且需要一个回调函数作为参数来获得user实例。setUser方法仅更新外键(本例中的user_id字段)的值为100,并保存这个Post模型类实例。通常,不管成功与否,当保存操作完成时,传递进去的回调函数都将被触发。
3.4.5 加载内嵌的数据
你或许想知道为什么调用User.load方法时,我们传递一个success方法,但当访问User的文章(Post)及评论(Comment)时我们并不需要这样做。这是因为上面的例子中,我们假定当发送请求以获取一个用户的信息时,服务器返回了该用户的数据及所有嵌套的文章和评论的数据。上例我们通过设置关联配置,框架在一次请求中就能自动解析出嵌套的数据。不是靠先发送一个请求获取用户数据,另一个请求获取文章数据,再发送其他请求以获取每篇文章的评论数据这种模式,我们可以在一次服务器响应中返回所有的数据,如下:
1. {
2. success: true,
3. users: [
4. {
5. id: 1,
6. name: 'Ed',
7. age: 25,
8. gender: 'male',
9. posts: [
10. {
11. id : 12,
12. title: 'All about data in Ext JS 4',
13. body : 'One areas that has seen the most improvement...',
14. comments: [
15. {
16. id: 123,
17. name: 'S Jobs',
18. message: 'One more thing'
19. }
20. ]
21. }
22. ]
23. }
24. ]
25.
26. }
27.
{
success: true,
users: [
{
id: 1,
name: 'Ed',
age: 25,
gender: 'male',
posts: [
{
id : 12,
title: 'All about data in Ext JS 4',
body : 'One areas that has seen the most improvement...',
comments: [
{
id: 123,
name: 'S Jobs',
message: 'One more thing'
}
]
}
]
}
]
}
这些数据将被框架自动解析出来。配置模型类的代理方式以用来加载任何地方的数据会变得很轻松,并且它们的阅读器模式几乎可以处理任何格式的响应数据。和Ext JS 3一样,模型类和存储类在整个框架中被许多组件使用,例如表格,树,表单。
查看关联和验证(Associations and Validations)的演示示例以获取一个可实际操作并且具有关联关系的模型实例。
当然,你可以以一种非嵌套的方式加载你的数据。如果你仅想需要时加载相关的数据,这种“懒惰加载”模式将可能是有效地。如前所做,我们仅加载User数据,除此之外,我们假定返回的响应仅包含User数据,没有任何相关联的文章(Post)数据。然后,我们在user.posts().load()方法添加回调函数中以获取相关的文章(Post)数据:
1. // Loads User with ID 1 User's Proxy
2. User.load(1, {
3. success: function(user) {
4. console.log("User: " + user.get('name'));
5. // Loads posts for user 1 using Post's Proxy
6. user.posts().load({
7. callback: function(posts, operation) {
8. Ext.each(posts, function(post) {
9. console.log("Comments for post: " + post.get('title'));
10. post.comments().each(function(comment) {
11. console.log(comment.get('message'));
12. });
13. });
14. }
15. });
16. }
17. });
18.
// Loads User with ID 1 User's Proxy
User.load(1, {
success: function(user) {
console.log("User: " + user.get('name'));
// Loads posts for user 1 using Post's Proxy
user.posts().load({
callback: function(posts, operation) {
Ext.each(posts, function(post) {
console.log("Comments for post: " + post.get('title'));
post.comments().each(function(comment) {
console.log(comment.get('message'));
});
});
}
});
}
});
查看懒惰关联(Lazy Associations)模式可以获取一个完整的示例
3.4.6 验证
自Ext JS 4起,模型类由于提供了验证数据的功能而变得更加丰富精彩了。为了证明这点,我们将在前面使用过的关联例子的基础上构建一个示例。首先让我们添加一些验证到User模型类中:
1. Ext.define('User', {
2. extend: 'Ext.data.Model',
3. fields: ...,
4. validations: [
5. {type: 'presence', name: 'name'},
6. {type: 'length', name: 'name', min: 5},
7. {type: 'format', name: 'age', matcher: /\d+/},
8. {type: 'inclusion', name: 'gender', list: ['male', 'female']},
9. {type: 'exclusion', name: 'name', list: ['admin']}
10. ],
11. proxy: ...
12. });
13.
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ...,
validations: [
{type: 'presence', name: 'name'},
{type: 'length', name: 'name', min: 5},
{type: 'format', name: 'age', matcher: /\d+/},
{type: 'inclusion', name: 'gender', list: ['male', 'female']},
{type: 'exclusion', name: 'name', list: ['admin']}
],
proxy: ...
});
验证和域定义遵循相同的代码格式。任何情况下,我们都可以指定一个域和一种验证类型。我们例子中的验证器配置要求name域必须存在,并且至少5个字符长,age域必须为数字,gender域的值只能为male或female,并且用户名可以为除了admin外的其他任何名称。一些验证器可能具有其他的可选配置 -- 例如,长度验证可以具有最大和最小属性,格式(format)可以具有匹配(matcher)属性等。Ext JS有五种内置的验证器,且可以轻松地添加用户自定义规则。首先让我们看看这五种类型:
· 存在(presence) 确保该域必须有确定值。0被看作有效值,但空字符串将不被视为有效值。
· 长度(length) 确保一个字符串长度位于最大和最小值之间。两个参数都是可选的。
· 格式(format) 确保一个字符串匹配指定的正则表达式。上例中我们确保age域必须为数字。
· 包含(inclusion) 确保该域的值在指定的数值集合中(例如确保性别只能是男或女)。
· 排除(exclusion) 确保该域的值不在指定的数值集合中(例如用户名不能为admin)
既然我们已经理解了不同验证器的功能,让我们尝试在一个User实例中使用它们。
我们创建一个user实例并在其中运行验证器,注意产生的任何错误:
1. // now lets try to create a new user with as many validation errors as we can
2. // 现在让我们尝试创建一个user实例,并产生尽量多的验证错误
3. var newUser = Ext.create('User', {
4. name: 'admin',
5. age: 'twenty-nine',
6. gender: 'not a valid gender'
7. });
8.
// now lets try to create a new user with as many validation errors as we can
// 现在让我们尝试创建一个user实例,并产生尽量多的验证错误
var newUser = Ext.create('User', {
name: 'admin',
age: 'twenty-nine',
gender: 'not a valid gender'
});
// run some validation on the new user we just created
// 在我们刚刚创建的user实例中运行一些验证器
var errors = newUser.validate();
console.log('Is User valid?', errors.isValid()); //returns 'false' as there were validation errors 当有验证器错误产生时,返回false
console.log('All Errors:', errors.items); //returns the array of all errors found on this model instance返回该模型类实例所有错误组合成的数组
console.log('Age Errors:', errors.getByField('age')); //returns the errors for the age field返回age域产生的错误
这里的关键函数是validate(),该函数运行所有配置验证器并返回一个Errors对象。这个简单的对象为所有错误的集合,并且添加了一些便利的方法,例如isValid() -- 当任何域都没有错误产生时,返回true,还有getByField()方法,返回给定域产生的所有错误。
4 示例
4.1 Windows的创建
Extjs4,创建Ext组件有了新的方式,就是Ext.create
Extjs4,创建Ext组件有了新的方式,就是Ext.create(....),而且可以使用动态加载JS的方式来加快组件的渲染,我们再也不必一次加载已经达到1MB的ext-all.js了,本文介绍如何在EXTJS4中创建一个window。
代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>窗口实例</title> <link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css" /> <script type="text/javascript" src="../extjs/bootstrap.js"></script> <script type="text/javascript" src="../extjs/locale/ext-lang-zh_CN.js"></script> <script type="text/jscript"> Ext.require('Ext.window'); Ext.onReady(function(){ Ext.create('Ext.Window',{ width:400, height:230, //X,Y标识窗口相对于父窗口的偏移值。 x:10, y:10, plain: true, //指示标题头的位置,分别为 top,bottom,left,right,默认为top headerPosition: 'left', title: 'ExtJS4 Window的创建,头在左' }).show(); Ext.create('Ext.Window',{ width:400, height:230, x:500, y:10, plain: true, //指示标题头的位置,分别为 top,bottom,left,right,默认为top headerPosition: 'right', title: 'ExtJS4 Window的创建,头在右' }).show(); Ext.create('Ext.Window',{ width:400, height:230, x:10, y:300, plain: true, //指示标题头的位置,分别为 top,bottom,left,right,默认为top headerPosition: 'bottom', title: 'ExtJS4 Window的创建,头在底' }).show(); var win = Ext.create('Ext.Window',{ width:400, height:230, x:500, y:300, plain: true, //指示标题头的位置,分别为 top,bottom,left,right,默认为top headerPosition: 'top', title: 'ExtJS4 Window的创建' }); win.show(); }); </script> </head> <body> </body> </html> |
运行后浏览器内可以看到四个窗口。
4.2 HBOX的使用
要使用HBox布局方式,首先的熟悉下一下几个主要属性:
一、align:字符类型,指示组件在容器内的对齐方式。有如下几种属性。
1、top(默认):排列于容器顶端。
2、middle:垂直居中排列于容器中。
3、stretch:垂直排列且拉伸义填补容器高度
4、stretchmax:垂直拉伸,并且组件以最高高度的组件为准。
二、flex:数字类型,指示组件在容器中水平呈现方式,通俗的讲,就是指示组件在容器中的相对宽度。
三、pack : 字符类型,指示组件在容器的位置,有如下几种属性。
1、start(默认):组件在容器左边
2、center:组件在容器中间
3、end:组件在容器的右边
实例代码:
Html代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>HBox实例</title> <link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css" /> <script type="text/javascript" src="../extjs/bootstrap.js"></script> <script type="text/javascript" src="../extjs/locale/ext-lang-zh_CN.js"></script> <script type="text/javascript" src="hbox.js"></script> </head> <body> </body> <div id="d1"></div> <div id="d2"></div> <div id="d3"></div> </html> |
Hbox.js
Ext.onReady(function(){ var d1 = Ext.create('Ext.Panel',{ title:'HBox 顶对齐,且组件在容器的左边', frame:true, width:600, height:100, items:[{ anchor:'100%', layout: { type:'hbox', padding:'10', pack:'start', align:'top' }, defaults:{margins:'0 5 0 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4' }] }] }) d1.render('d1'); var d2 = Ext.create('Ext.Panel',{ title:'HBox 垂直对齐,且组件在容器的右边', frame:true, width:600, height:100, items:[{ anchor:'100%', layout: { type:'hbox', padding:'10', align:'middle', pack:'end' }, defaults:{margins:'0 5 0 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4' }] }] }) d2.render('d2'); var d3 = Ext.create('Ext.Panel',{ title:'HBox 垂直水平居中,并且所有控件高度为最高控件的高度', frame:true, width:600, height:100, items:[{ anchor:'100%', layout: { type: 'hbox', padding:'5', align:'stretchmax', pack:'center' }, defaults:{margins:'0 5 0 0'}, items:[{ xtype:'button', text: 'Small Size' },{ xtype:'button', scale: 'medium', text: 'Medium Size' },{ xtype:'button', scale: 'large', text: 'Large Size' }] }] }) d3.render('d3'); }) |
运行结果:
4.3 VBOX的使用
要使用VBox布局方式,首先的熟悉下一下几个主要属性:
一、align:字符类型,指示组件在容器内的对齐方式。有如下几种属性。
1、left(默认):排列于容器左侧。
2、center :控件在容器水平居中。
3、stretch:控件横向拉伸至容器大小
4、stretchmax:控件横向拉伸,宽度为最宽控件的宽。
二、flex:数字类型,指示组件在容器中水平呈现方式,通俗的讲,就是指示组件在容器中的相对宽度。
三、pack : 字符类型,指示组件在容器的位置,有如下几种属性。
1、start(默认):组件在容器上边
2、center:组件在容器中间
3、end:组件在容器的下边
Html代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>VBox实例</title> <link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css" /> <script type="text/javascript" src="../extjs/bootstrap.js"></script> <script type="text/javascript" src="../extjs/locale/ext-lang-zh_CN.js"></script> <script type="text/javascript" src="vbox.js"></script> <style type="text/css"> html, body { font: normal 12px verdana; margin: 0; padding: 0; border: 0 none; } </style> </head> <body> </body> </html> |
Vbox.js
Ext.onReady(function() { var currentName; var replace = function(config, name) { var btns = Ext.getCmp('btns'); if (name && name != currentName) { currentName = name; btns.remove(0); btns.add(Ext.apply(config)); } }; var viewport = Ext.create('Ext.Viewport', { layout:'border', items: [{ id:'btns', region:'west', baseCls:'x-plain', split:true, width:150, minWidth: 100, maxWidth: 250, layout:'fit', margins: '5 0 5 5', items: { baseCls: 'x-plain', html: '<p style="padding:10px;color:#556677;font-size:11px;">点击右边的按钮查看效果</p>' } },{ region:'center', margins: '5 5 5 0', layout: 'anchor', items:[{ anchor: '100%', baseCls:'x-plain', layout: { type:'hbox', padding: 10 }, defaults:{ margins:'0 5 0 0', pressed: false, toggleGroup:'btns', allowDepress: false }, items: [{ xtype:'button', text: 'Spaced / Align: left', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'left' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'tbspacer', flex:1 },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4', margins:'0' } ] }, 'spaced'); } },{ xtype:'button', text: 'Multi-Spaced / Align: left', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'left' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'tbspacer', flex:1 },{ xtype:'button', text: 'Button 2' },{ xtype:'tbspacer', flex:1 },{ xtype:'button', text: 'Button 3' },{ xtype:'tbspacer', flex:1 },{ xtype:'button', text: 'Button 4', margins:'0' }] }, 'multi spaced - align left'); } },{ xtype:'button', text: 'Align: left', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'left' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4' }] }, 'align left'); } },{ xtype:'button', text: 'Align: center', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'center' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4' }] }, 'align center'); } },{ xtype:'button', text: 'Align: stretch', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'stretch' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4' }] }, 'align stretch'); } },{ xtype:'button', text: 'Align: stretchmax', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'stretchmax' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Jamie' },{ xtype:'button', text: 'Aaron' },{ xtype:'button', text: 'Tommy' },{ xtype:'button', text: 'Ed ' }] }, 'align stretchmax'); } }] },{ anchor: '100%', baseCls:'x-plain', layout: { type:'hbox', padding: '0 10 10' }, defaults:{ margins:'0 5 0 0', pressed: false, toggleGroup:'btns', allowDepress: false }, items: [{ xtype:'button', text: 'Flex: Even / Align: center', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'center' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1', flex:1 },{ xtype:'button', text: 'Button 2', flex:1 },{ xtype:'button', text: 'Button 3', flex:1 },{ xtype:'button', text: 'Button 4', flex:1, margins:'0' }] }, 'align flex even'); } },{ xtype:'button', text: 'Flex: Ratio / Align: center', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'center' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1', flex:1 },{ xtype:'button', text: 'Button 2', flex:1 },{ xtype:'button', text: 'Button 3', flex:1 },{ xtype:'button', text: 'Button 4', flex:3, margins:'0' }] }, 'align flex ratio'); } },{ xtype:'button', text: 'Flex + Stretch', handler: function(){ replace({ layout: { type:'vbox', padding:'5', align:'stretch' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1', flex:1 },{ xtype:'button', text: 'Button 2', flex:1 },{ xtype:'button', text: 'Button 3', flex:1 },{ xtype:'button', text: 'Button 4', flex:3, margins:'0' }] }, 'align flex + stretch'); } },{ xtype:'button', text: 'Pack: start / Align: center', handler: function(){ replace({ layout: { type:'vbox', padding:'5', pack:'start', align:'center' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4' }] }, 'align pack start + align center'); } },{ xtype:'button', text: 'Pack: center / Align: center', handler: function(){ replace({ layout: { type:'vbox', padding:'5', pack:'center', align:'center' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4', margins:'0' }] }, 'align pack center + align center'); } },{ xtype:'button', text: 'Pack: end / Align: center', handler: function(){ replace({ layout: { type:'vbox', padding:'5', pack:'end', align:'center' }, defaults:{margins:'0 0 5 0'}, items:[{ xtype:'button', text: 'Button 1' },{ xtype:'button', text: 'Button 2' },{ xtype:'button', text: 'Button 3' },{ xtype:'button', text: 'Button 4', margins:'0' }] }, 'align pack end + align center'); } }] }] }] }); }); |
4.4 Grid
Extjs4 Grid创建还是比较容易的,难记、难理解的地方,也就是数据的获取。下面,就创建一个最简单的Grid组件,后面,我们会逐渐丰富这个Grid的功能。
HTML代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Grid</title> <link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css" /> <script type="text/javascript" src="../extjs/bootstrap.js"></script> <script type="text/javascript" src="../extjs/locale/ext-lang-zh_CN.js"></script> <script type="text/javascript" src="grid.js"></script> </head> <body> <div id="demo"></div> </body> </html> |
Grid.js
Ext.require([ 'Ext.grid.*', 'Ext.data.*' ]); Ext.onReady(function(){ Ext.define('MyData',{ extend: 'Ext.data.Model', fields: [ //第一个字段需要指定mapping,其他字段,可以省略掉。 {name:'UserName',mapping:'UserName'}, 'Sex', 'Age', 'XueHao', 'BanJi' ] }); //创建数据源 var store = Ext.create('Ext.data.Store', { model: 'MyData', proxy: { //异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可 type: 'ajax', url: 'mydata.json', reader: { type: 'json', root: 'items', //totalProperty : 'total' } }, autoLoad: true }); //创建Grid var grid = Ext.create('Ext.grid.Panel',{ store: store, columns: [ {text: "姓名", width: 120, dataIndex: 'UserName', sortable: true}, {text: "性别", flex: 1, dataIndex: 'Sex', sortable: false}, {text: "年龄", width: 100, dataIndex: 'Age', sortable: true}, {text: "学号", width: 100, dataIndex: 'XueHao', sortable: true}, {text: "班级", width: 100, dataIndex: 'BanJi', sortable: true} ], height:400, width:480, x:20, y:40, title: 'ExtJS4 Grid示例', renderTo: 'demo', viewConfig: { stripeRows: true } }) }) |
Mydata.json
{ "items": [ { "UserName": "李彦宏", "Sex": "男", "Age":25, "XueHao":00001, "BanJi":"一班" }, { "UserName": "马云", "Sex": "男", "Age":31, "XueHao":00002, "BanJi":"二班" }, { "UserName": "张朝阳", "Sex": "男", "Age":30, "XueHao":00003, "BanJi":"一班" }, { "UserName": "朱俊", "Sex": "男", "Age":28, "XueHao":00004, "BanJi":"一班" }, { "UserName": "丁磊", "Sex": "男", "Age":29, "XueHao":00005, "BanJi":"二班" }, { "UserName": "李国军", "Sex": "男", "Age":30, "XueHao":00006, "BanJi":"二班" }, { "UserName": "王志宝", "Sex": "男", "Age":25, "XueHao":00007, "BanJi":"一班" } ] } |
这里需要注意的是json文件的编码方式必须要跟html文件的编码方式一致,如本例中html的编码方式为utf-8,则json必须也要是utf-8格式,否则页面数据会出现乱码。Json仅仅是个文本文件而已,转码方式有很多。
Extjs4 Grid组件的数据需要几点注意。
第一,就是数据类型,我们可以指定数据类型,比如:
var store = Ext.create('Ext.data.ArrayStore', { fields: [ {name: 'company'}, {name: 'price', type: 'float'}, {name: 'change', type: 'float'}, {name: 'pctChange', type: 'float'}, {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'} ], data: myData }); |
var store = Ext.create('Ext.data.ArrayStore', {
fields: [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
],
data: myData
});
数据类型分为以下几种:
1、auto(默认)
2、string
3、int
4、float
5、boolean
6、date
第二:Ext.data.Model,示例中,只指定了一个mapping,那么第一个mapping是必须要指定的,从第二个开始,我们就不需要再去指定mapping了,Extjs很聪明,他会根据数据来判断需要的mapping。
运行结果:
4.5 Grid分页
Grid组件,当数据量很大的时候,就需要分页显示,本文介绍如何实现Extjs4 Grid的分页功能。
先看THML代码:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <title>Paging Grid-MHZG.NET</title>
6. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
7. <script type="text/javascript" src="../../bootstrap.js"></script>
8. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
9. <script type="text/javascript" src="paing.js"></script>
10. </head>
11.
12. <body>
13. <div id="demo"></div>
14. </body>
15. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Paging Grid-MHZG.NET</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="paing.js"></script>
</head>
<body>
<div id="demo"></div>
</body>
</html>
这里引用的JS文件,都是相对于Extjs4整个目录的。如果已经将JS和CSS文件剥离并分目录放置了,那么一定要注意修改bootstrap.js。
JS代码:
1. Ext.require([
2. 'Ext.grid.*',
3. 'Ext.toolbar.Paging',
4. 'Ext.data.*'
5. ]);
6.
7. Ext.onReady(function(){
8. Ext.define('MyData',{
9. extend: 'Ext.data.Model',
10. fields: [
11. 'title','author',
12. //第一个字段需要指定mapping,其他字段,可以省略掉。
13. {name:'hits',type: 'int'},
14. 'addtime'
15. ]
16. });
17.
18. //创建数据源
19. var store = Ext.create('Ext.data.Store', {
20. //分页大小
21. pageSize: 50,
22. model: 'MyData',
23. //是否在服务端排序
24. remoteSort: true,
25. proxy: {
26. //异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
27. type: 'ajax',
28. url: 'mydata.asp',
29.
30. reader: {
31. root: 'items',
32. totalProperty : 'total'
33. },
34. simpleSortMode: true
35. },
36. sorters: [{
37. //排序字段。
38. property: 'hits',
39. //排序类型,默认为 ASC
40. direction: 'DESC'
41. }]
42. });
43.
44. //创建Grid
45. var grid = Ext.create('Ext.grid.Panel',{
46. store: store,
47. columns: [
48. {text: "标题", width: 120, dataIndex: 'title', sortable: true},
49. {text: "作者", flex: 200, dataIndex: 'author', sortable: false},
50. {text: "点击数", width: 100, dataIndex: 'hits', sortable: true},
51. {text: "添加时间", width: 100, dataIndex: 'addtime', sortable: true}
52. ],
53. height:400,
54. width:520,
55. x:20,
56. y:40,
57. title: 'ExtJS4 Grid 分页示例',
58. disableSelection: true,
59. loadMask: true,
60. renderTo: 'demo',
61. viewConfig: {
62. id: 'gv',
63. trackOver: false,
64. stripeRows: false
65. },
66.
67. bbar: Ext.create('Ext.PagingToolbar', {
68. store: store,
69. displayInfo: true,
70. displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
71. emptyMsg: "没有数据"
72. })
73. })
74. store.loadPage(1);
75. })
Ext.require([
'Ext.grid.*',
'Ext.toolbar.Paging',
'Ext.data.*'
]);
Ext.onReady(function(){
Ext.define('MyData',{
extend: 'Ext.data.Model',
fields: [
'title','author',
//第一个字段需要指定mapping,其他字段,可以省略掉。
{name:'hits',type: 'int'},
'addtime'
]
});
//创建数据源
var store = Ext.create('Ext.data.Store', {
//分页大小
pageSize: 50,
model: 'MyData',
//是否在服务端排序
remoteSort: true,
proxy: {
//异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
type: 'ajax',
url: 'mydata.asp',
reader: {
root: 'items',
totalProperty : 'total'
},
simpleSortMode: true
},
sorters: [{
//排序字段。
property: 'hits',
//排序类型,默认为 ASC
direction: 'DESC'
}]
});
//创建Grid
var grid = Ext.create('Ext.grid.Panel',{
store: store,
columns: [
{text: "标题", width: 120, dataIndex: 'title', sortable: true},
{text: "作者", flex: 200, dataIndex: 'author', sortable: false},
{text: "点击数", width: 100, dataIndex: 'hits', sortable: true},
{text: "添加时间", width: 100, dataIndex: 'addtime', sortable: true}
],
height:400,
width:520,
x:20,
y:40,
title: 'ExtJS4 Grid 分页示例',
disableSelection: true,
loadMask: true,
renderTo: 'demo',
viewConfig: {
id: 'gv',
trackOver: false,
stripeRows: false
},
bbar: Ext.create('Ext.PagingToolbar', {
store: store,
displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
emptyMsg: "没有数据"
})
})
store.loadPage(1);
})
关于数据,任何服务端都可以,只要返回相应的数据就可以了。这里简单使用ASP代码做了一些测试用的数据,但是这些测试代码包含了参数接收、基本判断以及分页方法。具体情况具体实施,这些代码只作为抛砖引玉的作用。
ASP代码:
1. <%
2. Response.ContentType = "text/html"
3. Response.Charset = "UTF-8"
4. %>
5. <%
6. '返回JSON数据,自定义一些测试数据。。
7. '这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
8. '由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
9. start = Request("start")
10. limit = Request("limit")
11. If start = "" Then
12. start = 0
13. End If
14. If limit = "" Then
15. limit = 50
16. End If
17. sorts = Replace(Trim(Request.Form("sort")),"'","")
18. dir = Replace(Trim(Request.Form("dir")),"'","")
19.
20. Dim counts:counts=300
21. '注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
22.
23. Dim Ls:Ls = Cint(start) + Cint(limit)
24. If Ls >= counts Then
25. Ls = counts
26. End If
27.
28. Echo("{")
29. Echo("""total"":")
30. Echo(""""&counts&""",")
31. Echo("""items"":[")
32. For i = start+1 To Ls
33. Echo("{")
34. Echo("""title"":""newstitle"&i&"""")
35. Echo(",")
36. Echo("""author"":""author"&i&"""")
37. Echo(",")
38. Echo("""hits"":"""&i&"""")
39. Echo(",")
40. Echo("""addtime"":"""&Now()&"""")
41. Echo("}")
42. If i<Ls Then
43. Echo(",")
44. End If
45. Next
46. Echo("]}")
47. Function Echo(str)
48. Response.Write(str)
49. End Function
50. %>
<%
Response.ContentType = "text/html"
Response.Charset = "UTF-8"
%>
<%
'返回JSON数据,自定义一些测试数据。。
'这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
'由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
start = Request("start")
limit = Request("limit")
If start = "" Then
start = 0
End If
If limit = "" Then
limit = 50
End If
sorts = Replace(Trim(Request.Form("sort")),"'","")
dir = Replace(Trim(Request.Form("dir")),"'","")
Dim counts:counts=300
'注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
Dim Ls:Ls = Cint(start) + Cint(limit)
If Ls >= counts Then
Ls = counts
End If
Echo("{")
Echo("""total"":")
Echo(""""&counts&""",")
Echo("""items"":[")
For i = start+1 To Ls
Echo("{")
Echo("""title"":""newstitle"&i&"""")
Echo(",")
Echo("""author"":""author"&i&"""")
Echo(",")
Echo("""hits"":"""&i&"""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
If i<Ls Then
Echo(",")
End If
Next
Echo("]}")
Function Echo(str)
Response.Write(str)
End Function
%>
最后,来张效果图:
4.6 多表头grid
做项目的时候,有时候会遇到多表头的Grid,在EXTJS4中,多表头的实现已经很简单了,本文介绍如何实现多表头gird的功能。
先看下效果图:
实现代码如下:
HTML代码:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <title>GroupHeaderGrid-MHZG.NET</title>
6. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
7. <script type="text/javascript" src="../../bootstrap.js"></script>
8. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
9. <script type="text/javascript" src="group-header.js"></script>
10. </head>
11.
12. <body>
13. <div id="demo"></div>
14. </body>
15. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>GroupHeaderGrid-MHZG.NET</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="group-header.js"></script>
</head>
<body>
<div id="demo"></div>
</body>
</html>
group-header.js:
1. Ext.require([
2. 'Ext.grid.*',
3. 'Ext.toolbar.Paging',
4. 'Ext.util.*',
5. 'Ext.data.*'
6. ]);
7.
8. Ext.onReady(function(){
9. Ext.define('MyData',{
10. extend: 'Ext.data.Model',
11. fields: [
12. 'title','author',
13. //第一个字段需要指定mapping,其他字段,可以省略掉。
14. {name:'hits',type: 'int'},
15. 'addtime'
16. ]
17. });
18.
19. //创建数据源
20. var store = Ext.create('Ext.data.Store', {
21. //分页大小
22. pageSize: 50,
23. model: 'MyData',
24. //是否在服务端排序
25. remoteSort: true,
26. proxy: {
27. //异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
28. type: 'ajax',
29. url: 'mydata.asp',
30.
31. reader: {
32. root: 'items',
33. totalProperty : 'total'
34. },
35. simpleSortMode: true
36. },
37. sorters: [{
38. //排序字段。
39. property: 'hits',
40. //排序类型,默认为 ASC
41. direction: 'DESC'
42. }]
43. });
44.
45. //创建Grid
46. var grid = Ext.create('Ext.grid.Panel',{
47. store: store,
48. columnLines: true,
49. columns: [{
50. text:"基本信息",
51. columns:[
52. {text: "标题", width: 120, dataIndex: 'title', sortable: true},
53. {text: "作者", width: 200, dataIndex: 'author', sortable: false},
54. {text: "点击数", width: 100, dataIndex: 'hits', sortable: true}]
55. },
56. {text: "添加时间", width: 100, dataIndex: 'addtime', sortable: true}
57. ],
58. height:400,
59. width:520,
60. x:20,
61. y:40,
62. title: 'ExtJS4 多表头Grid带分页示例',
63. disableSelection: true,
64. loadMask: true,
65. renderTo: 'demo',
66. viewConfig: {
67. id: 'gv',
68. trackOver: false,
69. stripeRows: false
70. },
71.
72. bbar: Ext.create('Ext.PagingToolbar', {
73. store: store,
74. displayInfo: true,
75. displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
76. emptyMsg: "没有数据"
77. })
78. })
79. store.loadPage(1);
80. })
Ext.require([
'Ext.grid.*',
'Ext.toolbar.Paging',
'Ext.util.*',
'Ext.data.*'
]);
Ext.onReady(function(){
Ext.define('MyData',{
extend: 'Ext.data.Model',
fields: [
'title','author',
//第一个字段需要指定mapping,其他字段,可以省略掉。
{name:'hits',type: 'int'},
'addtime'
]
});
//创建数据源
var store = Ext.create('Ext.data.Store', {
//分页大小
pageSize: 50,
model: 'MyData',
//是否在服务端排序
remoteSort: true,
proxy: {
//异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
type: 'ajax',
url: 'mydata.asp',
reader: {
root: 'items',
totalProperty : 'total'
},
simpleSortMode: true
},
sorters: [{
//排序字段。
property: 'hits',
//排序类型,默认为 ASC
direction: 'DESC'
}]
});
//创建Grid
var grid = Ext.create('Ext.grid.Panel',{
store: store,
columnLines: true,
columns: [{
text:"基本信息",
columns:[
{text: "标题", width: 120, dataIndex: 'title', sortable: true},
{text: "作者", width: 200, dataIndex: 'author', sortable: false},
{text: "点击数", width: 100, dataIndex: 'hits', sortable: true}]
},
{text: "添加时间", width: 100, dataIndex: 'addtime', sortable: true}
],
height:400,
width:520,
x:20,
y:40,
title: 'ExtJS4 多表头Grid带分页示例',
disableSelection: true,
loadMask: true,
renderTo: 'demo',
viewConfig: {
id: 'gv',
trackOver: false,
stripeRows: false
},
bbar: Ext.create('Ext.PagingToolbar', {
store: store,
displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
emptyMsg: "没有数据"
})
})
store.loadPage(1);
})
JS代码要注意的地方:
1、记得载入'Ext.util.*',
2、二级表头必须指定宽度,如果不指定的话,会提示错误。如图所示,红框里的项,必须要指定宽度。
服务端代码,这里使用ASP编写,具体解释请参考前一篇文章。
1. <%
2. Response.ContentType = "text/html"
3. Response.Charset = "UTF-8"
4. %>
5. <%
6. '返回JSON数据,自定义一些测试数据。。
7. '这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
8. '由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
9. start = Request("start")
10. limit = Request("limit")
11. If start = "" Then
12. start = 0
13. End If
14. If limit = "" Then
15. limit = 50
16. End If
17. sorts = Replace(Trim(Request.Form("sort")),"'","")
18. dir = Replace(Trim(Request.Form("dir")),"'","")
19.
20. Dim counts:counts=300
21. '注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
22.
23. Dim Ls:Ls = Cint(start) + Cint(limit)
24. If Ls >= counts Then
25. Ls = counts
26. End If
27.
28. Echo("{")
29. Echo("""total"":")
30. Echo(""""&counts&""",")
31. Echo("""items"":[")
32. For i = start+1 To Ls
33. Echo("{")
34. Echo("""title"":""newstitle"&i&"""")
35. Echo(",")
36. Echo("""author"":""author"&i&"""")
37. Echo(",")
38. Echo("""hits"":"""&i&"""")
39. Echo(",")
40. Echo("""addtime"":"""&Now()&"""")
41. Echo("}")
42. If i<Ls Then
43. Echo(",")
44. End If
45. Next
46. Echo("]}")
47. Function Echo(str)
48. Response.Write(str)
49. End Function
50. %>
<%
Response.ContentType = "text/html"
Response.Charset = "UTF-8"
%>
<%
'返回JSON数据,自定义一些测试数据。。
'这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
'由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
start = Request("start")
limit = Request("limit")
If start = "" Then
start = 0
End If
If limit = "" Then
limit = 50
End If
sorts = Replace(Trim(Request.Form("sort")),"'","")
dir = Replace(Trim(Request.Form("dir")),"'","")
Dim counts:counts=300
'注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
Dim Ls:Ls = Cint(start) + Cint(limit)
If Ls >= counts Then
Ls = counts
End If
Echo("{")
Echo("""total"":")
Echo(""""&counts&""",")
Echo("""items"":[")
For i = start+1 To Ls
Echo("{")
Echo("""title"":""newstitle"&i&"""")
Echo(",")
Echo("""author"":""author"&i&"""")
Echo(",")
Echo("""hits"":"""&i&"""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
If i<Ls Then
Echo(",")
End If
Next
Echo("]}")
Function Echo(str)
Response.Write(str)
End Function
%>
4.7 带搜索的grid
项目开发中,Grid组件少不了搜索功能,在Extjs4中,搜索组件以插件的形式出现,而且实现也非常简单,搜索组件位于examples/ux/form目录下,JS文件是SearchField.js。
Grid加载搜索功能,要注意的是:
1、开启延迟加载,即Ext.Loader.setConfig({enabled: true});
2、设置插件的目录:Ext.Loader.setPath('Ext.ux', '../../examples/ux'); ,需要注意,插件所在目录一定要正确,否则加载失败,就实现不了所要功能了。
效果图:
初始加载
输入查询条件后
HTML代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>SearchGrid-MHZG.NET</title> <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" /> <style type="text/css"> #search-results a { color: #385F95; font:bold 11px tahoma, arial, helvetica, sans-serif; text-decoration:none; } #search-results a:hover { text-decoration:underline; } #search-results p { margin:3px !important; } .search-item { font:normal 11px tahoma, arial, helvetica, sans-serif; padding:3px 10px 3px 10px; border:1px solid #fff; border-bottom:1px solid #eeeeee; white-space:normal; color:#555; } .search-item h3 { display:block; font:inherit; font-weight:bold; color:#222; } .search-item h3 span { float: right; font-weight:normal; margin:0 0 5px 5px; width:100px; display:block; clear:none; } /*这里要注意这两张图片的路径要正确*/ .x-form-clear-trigger { background-image: url(../../resources/themes/images/default/form/clear-trigger.gif); } .x-form-search-trigger { background-image: url(../../resources/themes/images/default/form/search-trigger.gif); } </style> <script type="text/javascript" src="../../bootstrap.js"></script> <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script> <script type="text/javascript" src="searchgrid.js"></script> </head> <body> <div id="demo"></div> </body> </html> |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SearchGrid-MHZG.NET</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<style type="text/css">
#search-results a {
color: #385F95;
font:bold 11px tahoma, arial, helvetica, sans-serif;
text-decoration:none;
}
#search-results a:hover {
text-decoration:underline;
}
#search-results p {
margin:3px !important;
}
.search-item {
font:normal 11px tahoma, arial, helvetica, sans-serif;
padding:3px 10px 3px 10px;
border:1px solid #fff;
border-bottom:1px solid #eeeeee;
white-space:normal;
color:#555;
}
.search-item h3 {
display:block;
font:inherit;
font-weight:bold;
color:#222;
}
.search-item h3 span {
float: right;
font-weight:normal;
margin:0 0 5px 5px;
width:100px;
display:block;
clear:none;
}
/*这里要注意这两张图片的路径要正确*/
.x-form-clear-trigger {
background-image: url(../../resources/themes/images/default/form/clear-trigger.gif);
}
.x-form-search-trigger {
background-image: url(../../resources/themes/images/default/form/search-trigger.gif);
}
</style>
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="searchgrid.js"></script>
</head>
<body>
<div id="demo"></div>
</body>
</html>
SearchGrid.js:
1. Ext.Loader.setConfig({enabled: true});
2. Ext.Loader.setPath('Ext.ux', '../../examples/ux');
3. Ext.require([
4. 'Ext.grid.*',
5. 'Ext.toolbar.Paging',
6. 'Ext.util.*',
7. 'Ext.data.*',
8. 'Ext.ux.form.SearchField'
9. ]);
10.
11. Ext.onReady(function(){
12. Ext.define('MyData',{
13. extend: 'Ext.data.Model',
14. fields: [
15. 'title','author',
16. //第一个字段需要指定mapping,其他字段,可以省略掉。
17. {name:'hits',type: 'int'},
18. 'addtime'
19. ]
20. });
21.
22. //创建数据源
23. var store = Ext.create('Ext.data.Store', {
24. //分页大小
25. pageSize: 50,
26. model: 'MyData',
27. //是否在服务端排序
28. remoteSort: true,
29. proxy: {
30. //异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
31. type: 'ajax',
32. url: 'searchgrid.asp',
33.
34. reader: {
35. root: 'items',
36. totalProperty : 'total'
37. },
38. simpleSortMode: true
39. },
40. sorters: [{
41. //排序字段。
42. property: 'hits',
43. //排序类型,默认为 ASC
44. direction: 'DESC'
45. }]
46. });
47.
48. //创建Grid
49. var grid = Ext.create('Ext.grid.Panel',{
50. store: store,
51. columnLines: true,
52. columns: [
53. {text: "标题", width: 120, dataIndex: 'title', sortable: true},
54. {text: "作者", width: 140, dataIndex: 'author', sortable: false},
55. {text: "点击数", width: 100, dataIndex: 'hits', sortable: true},
56. {text: "添加时间", width: 150, dataIndex: 'addtime', sortable: true}
57. ],
58. height:400,
59. width:520,
60. x:20,
61. y:40,
62. title: 'ExtJS4 SearchGrid-Grid 搜索',
63. disableSelection: true,
64. loadMask: true,
65. renderTo: 'demo',
66. viewConfig: {
67. id: 'gv',
68. trackOver: false,
69. stripeRows: false
70. },
71. dockedItems: [{
72. dock: 'top',
73. xtype: 'toolbar',
74. items: {
75. width: 300,
76. fieldLabel: '搜索',
77. labelWidth: 50,
78. xtype: 'searchfield',
79. store: store
80. }
81. }, {
82. dock: 'bottom',
83. xtype: 'pagingtoolbar',
84. store: store,
85. pageSize: 25,
86. displayInfo: true,
87. displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
88. emptyMsg: '没有数据'
89. }]
90.
91. })
92. store.loadPage(1);
93. })
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', '../../examples/ux');
Ext.require([
'Ext.grid.*',
'Ext.toolbar.Paging',
'Ext.util.*',
'Ext.data.*',
'Ext.ux.form.SearchField'
]);
Ext.onReady(function(){
Ext.define('MyData',{
extend: 'Ext.data.Model',
fields: [
'title','author',
//第一个字段需要指定mapping,其他字段,可以省略掉。
{name:'hits',type: 'int'},
'addtime'
]
});
//创建数据源
var store = Ext.create('Ext.data.Store', {
//分页大小
pageSize: 50,
model: 'MyData',
//是否在服务端排序
remoteSort: true,
proxy: {
//异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
type: 'ajax',
url: 'searchgrid.asp',
reader: {
root: 'items',
totalProperty : 'total'
},
simpleSortMode: true
},
sorters: [{
//排序字段。
property: 'hits',
//排序类型,默认为 ASC
direction: 'DESC'
}]
});
//创建Grid
var grid = Ext.create('Ext.grid.Panel',{
store: store,
columnLines: true,
columns: [
{text: "标题", width: 120, dataIndex: 'title', sortable: true},
{text: "作者", width: 140, dataIndex: 'author', sortable: false},
{text: "点击数", width: 100, dataIndex: 'hits', sortable: true},
{text: "添加时间", width: 150, dataIndex: 'addtime', sortable: true}
],
height:400,
width:520,
x:20,
y:40,
title: 'ExtJS4 SearchGrid-Grid 搜索',
disableSelection: true,
loadMask: true,
renderTo: 'demo',
viewConfig: {
id: 'gv',
trackOver: false,
stripeRows: false
},
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
items: {
width: 300,
fieldLabel: '搜索',
labelWidth: 50,
xtype: 'searchfield',
store: store
}
}, {
dock: 'bottom',
xtype: 'pagingtoolbar',
store: store,
pageSize: 25,
displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
emptyMsg: '没有数据'
}]
})
store.loadPage(1);
})
代码完成了Grid组件异步加载信息、分页和搜索功能,可以满足一般使用情况了。
服务端代码,由于使用测试数据,这里只使用了最简单的方法来实现搜索效果,实际操作中,需要将查询条件置于SQL语句中,达到搜索效果。
SearchGrid.asp:
1. <%
2. Response.ContentType = "text/html"
3. Response.Charset = "UTF-8"
4. %>
5. <%
6. '返回JSON数据,自定义一些测试数据。。
7. '这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
8. '由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
9. start = Request("start")
10. limit = Request("limit")
11. '查询时获取的参数。
12. query = Request("query")
13. If start = "" Then
14. start = 0
15. End If
16. If limit = "" Then
17. limit = 50
18. End If
19. sorts = Replace(Trim(Request.Form("sort")),"'","")
20. dir = Replace(Trim(Request.Form("dir")),"'","")
21.
22. '测试数据,这里直接输出结果,实际应用中,应该把查询条件放到SQL语句中。
23. If query = "newstitle" Then
24. Echo("{")
25. Echo("""total"":")
26. Echo("""1")
27. Echo(""",""items"":[")
28. Echo("{")
29. Echo("""title"":""newstitle""")
30. Echo(",")
31. Echo("""author"":""author""")
32. Echo(",")
33. Echo("""hits"":""100""")
34. Echo(",")
35. Echo("""addtime"":"""&Now()&"""")
36. Echo("}")
37. Echo("]}")
38. Else
39. Dim counts:counts=300
40. '注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
41.
42. Dim Ls:Ls = Cint(start) + Cint(limit)
43. If Ls >= counts Then
44. Ls = counts
45. End If
46.
47. Echo("{")
48. Echo("""total"":")
49. Echo(""""&counts&""",")
50. Echo("""items"":[")
51. For i = start+1 To Ls
52. Echo("{")
53. Echo("""title"":""newstitle"&i&"""")
54. Echo(",")
55. Echo("""author"":""author"&i&"""")
56. Echo(",")
57. Echo("""hits"":"""&i&"""")
58. Echo(",")
59. Echo("""addtime"":"""&Now()&"""")
60. Echo("}")
61. If i<Ls Then
62. Echo(",")
63. End If
64. Next
65.
66. Echo("]}")
67. End If
68. Function Echo(str)
69. Response.Write(str)
70. End Function
71. %>
<%
Response.ContentType = "text/html"
Response.Charset = "UTF-8"
%>
<%
'返回JSON数据,自定义一些测试数据。。
'这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
'由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
start = Request("start")
limit = Request("limit")
'查询时获取的参数。
query = Request("query")
If start = "" Then
start = 0
End If
If limit = "" Then
limit = 50
End If
sorts = Replace(Trim(Request.Form("sort")),"'","")
dir = Replace(Trim(Request.Form("dir")),"'","")
'测试数据,这里直接输出结果,实际应用中,应该把查询条件放到SQL语句中。
If query = "newstitle" Then
Echo("{")
Echo("""total"":")
Echo("""1")
Echo(""",""items"":[")
Echo("{")
Echo("""title"":""newstitle""")
Echo(",")
Echo("""author"":""author""")
Echo(",")
Echo("""hits"":""100""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
Echo("]}")
Else
Dim counts:counts=300
'注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
Dim Ls:Ls = Cint(start) + Cint(limit)
If Ls >= counts Then
Ls = counts
End If
Echo("{")
Echo("""total"":")
Echo(""""&counts&""",")
Echo("""items"":[")
For i = start+1 To Ls
Echo("{")
Echo("""title"":""newstitle"&i&"""")
Echo(",")
Echo("""author"":""author"&i&"""")
Echo(",")
Echo("""hits"":"""&i&"""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
If i<Ls Then
Echo(",")
End If
Next
Echo("]}")
End If
Function Echo(str)
Response.Write(str)
End Function
%>
至此,带搜索功能的Grid就全部完成了。
4.8 Grid多选/全选
但大量数据需要删除操作的时候,总不能一条一条去删除吧,本文介绍如何在Extjs4 Grid中加入全选功能。
先来张效果图:
点击显示所选后:
注意点:
1、需要在JS文件中动态加载“'Ext.selection.CheckboxModel'”
2、服务端要返回数据的id值。
3、Ext.data.Model中,要有id的信息,就是因为JS代码中忘记了写id,导致调试了很久都无法获取id值,从头检查代码的时候才发现错误。
正文:
html代码:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <title>Grid多选/全选-MHZG.NET</title>
6. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
7. <style type="text/css">
8. #search-results a {
9. color: #385F95;
10. font:bold 11px tahoma, arial, helvetica, sans-serif;
11. text-decoration:none;
12. }
13. .add {
14. background-image:url(../../examples/shared/icons/fam/cog.gif) !important;
15. }
16. #search-results a:hover {
17. text-decoration:underline;
18. }
19.
20. #search-results p {
21. margin:3px !important;
22. }
23.
24. .search-item {
25. font:normal 11px tahoma, arial, helvetica, sans-serif;
26. padding:3px 10px 3px 10px;
27. border:1px solid #fff;
28. border-bottom:1px solid #eeeeee;
29. white-space:normal;
30. color:#555;
31. }
32. .search-item h3 {
33. display:block;
34. font:inherit;
35. font-weight:bold;
36. color:#222;
37. }
38.
39. .search-item h3 span {
40. float: right;
41. font-weight:normal;
42. margin:0 0 5px 5px;
43. width:100px;
44. display:block;
45. clear:none;
46. }
47.
48. .x-form-clear-trigger {
49. background-image: url(../../resources/themes/images/default/form/clear-trigger.gif);
50. }
51.
52. .x-form-search-trigger {
53. background-image: url(../../resources/themes/images/default/form/search-trigger.gif);
54. }
55. </style>
56. <script type="text/javascript" src="../../bootstrap.js"></script>
57. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
58. <script type="text/javascript" src="selectgrid.js"></script>
59. </head>
60.
61. <body>
62. <div id="demo"></div>
63. </body>
64. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Grid多选/全选-MHZG.NET</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<style type="text/css">
#search-results a {
color: #385F95;
font:bold 11px tahoma, arial, helvetica, sans-serif;
text-decoration:none;
}
.add {
background-image:url(../../examples/shared/icons/fam/cog.gif) !important;
}
#search-results a:hover {
text-decoration:underline;
}
#search-results p {
margin:3px !important;
}
.search-item {
font:normal 11px tahoma, arial, helvetica, sans-serif;
padding:3px 10px 3px 10px;
border:1px solid #fff;
border-bottom:1px solid #eeeeee;
white-space:normal;
color:#555;
}
.search-item h3 {
display:block;
font:inherit;
font-weight:bold;
color:#222;
}
.search-item h3 span {
float: right;
font-weight:normal;
margin:0 0 5px 5px;
width:100px;
display:block;
clear:none;
}
.x-form-clear-trigger {
background-image: url(../../resources/themes/images/default/form/clear-trigger.gif);
}
.x-form-search-trigger {
background-image: url(../../resources/themes/images/default/form/search-trigger.gif);
}
</style>
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="selectgrid.js"></script>
</head>
<body>
<div id="demo"></div>
</body>
</html>
selectgrid.js:
1. Ext.Loader.setConfig({enabled: true});
2. Ext.Loader.setPath('Ext.ux', '../../examples/ux');
3. Ext.require([
4. 'Ext.grid.*',
5. 'Ext.toolbar.Paging',
6. 'Ext.util.*',
7. 'Ext.data.*',
8. 'Ext.ux.form.SearchField',
9. 'Ext.selection.CheckboxModel'
10. ]);
11.
12. Ext.onReady(function(){
13. Ext.define('MyData',{
14. extend: 'Ext.data.Model',
15. fields: [
16. 'id','title','author',
17. {name:'hits',type: 'int'},
18. 'addtime'
19. ]
20. });
21.
22. //创建数据源
23. var store = Ext.create('Ext.data.Store', {
24. //分页大小
25. pageSize: 50,
26. model: 'MyData',
27. //是否在服务端排序
28. remoteSort: true,
29. proxy: {
30. //异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
31. type: 'ajax',
32. url: 'selectgrid.asp',
33.
34. reader: {
35. root: 'items',
36. totalProperty : 'total'
37. },
38. simpleSortMode: true
39. },
40. sorters: [{
41. //排序字段。
42. property: 'hits',
43. //排序类型,默认为 ASC
44. direction: 'DESC'
45. }]
46. });
47.
48. //创建多选
49. var selModel = Ext.create('Ext.selection.CheckboxModel');
50. //创建Grid
51. var grid = Ext.create('Ext.grid.Panel',{
52. store: store,
53. selModel: selModel,
54. columnLines: true,
55. columns: [
56. {text: "标题", width: 120, dataIndex: 'title', sortable: true},
57. {text: "作者", width: 140, dataIndex: 'author', sortable: false},
58. {text: "点击数", width: 100, dataIndex: 'hits', sortable: true},
59. {text: "添加时间", width: 150, dataIndex: 'addtime', sortable: true}
60. ],
61. height:400,
62. width:520,
63. x:20,
64. y:40,
65. title: 'ExtJS4 SearchGrid-Grid 搜索',
66.
67. disableSelection: false,//值为TRUE,表示禁止选择行
68. frame: true,
69. loadMask: true,
70. renderTo: 'demo',
71. viewConfig: {
72. id: 'gv',
73. trackOver: false,
74. stripeRows: false
75. },
76. dockedItems: [{
77. dock: 'top',
78. xtype: 'toolbar',
79. items: [{
80. itemId: 'Button',
81. text:'显示所选',
82. tooltip:'Add a new row',
83. iconCls:'add',
84. handler:function(){
85. var record = grid.getSelectionModel().getSelection();
86. if(record.length == 0){
87. Ext.MessageBox.show({
88. title:"提示",
89. msg:"请先选择您要操作的行!"
90. //icon: Ext.MessageBox.INFO
91. })
92. return;
93. }else{
94. var ids = "";
95. for(var i = 0; i < record.length; i++){
96. ids += record[i].get("id")
97. if(i<record.length-1){
98. ids = ids + ",";
99. }
100. }
101. Ext.MessageBox.show({
102. title:"所选ID列表",
103. msg:ids
104. //icon: Ext.MessageBox.INFO
105. })
106. }
107. }
108. },'-',{
109. width: 300,
110. fieldLabel: '搜索',
111. labelWidth: 50,
112. xtype: 'searchfield',
113. store: store
114. }]
115. }, {
116. dock: 'bottom',
117. xtype: 'pagingtoolbar',
118. store: store,
119. pageSize: 25,
120. displayInfo: true,
121. displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
122. emptyMsg: '没有数据'
123. }]
124.
125. })
126. store.loadPage(1);
127. })
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', '../../examples/ux');
Ext.require([
'Ext.grid.*',
'Ext.toolbar.Paging',
'Ext.util.*',
'Ext.data.*',
'Ext.ux.form.SearchField',
'Ext.selection.CheckboxModel'
]);
Ext.onReady(function(){
Ext.define('MyData',{
extend: 'Ext.data.Model',
fields: [
'id','title','author',
{name:'hits',type: 'int'},
'addtime'
]
});
//创建数据源
var store = Ext.create('Ext.data.Store', {
//分页大小
pageSize: 50,
model: 'MyData',
//是否在服务端排序
remoteSort: true,
proxy: {
//异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
type: 'ajax',
url: 'selectgrid.asp',
reader: {
root: 'items',
totalProperty : 'total'
},
simpleSortMode: true
},
sorters: [{
//排序字段。
property: 'hits',
//排序类型,默认为 ASC
direction: 'DESC'
}]
});
//创建多选
var selModel = Ext.create('Ext.selection.CheckboxModel');
//创建Grid
var grid = Ext.create('Ext.grid.Panel',{
store: store,
selModel: selModel,
columnLines: true,
columns: [
{text: "标题", width: 120, dataIndex: 'title', sortable: true},
{text: "作者", width: 140, dataIndex: 'author', sortable: false},
{text: "点击数", width: 100, dataIndex: 'hits', sortable: true},
{text: "添加时间", width: 150, dataIndex: 'addtime', sortable: true}
],
height:400,
width:520,
x:20,
y:40,
title: 'ExtJS4 SearchGrid-Grid 搜索',
disableSelection: false,//值为TRUE,表示禁止选择行
frame: true,
loadMask: true,
renderTo: 'demo',
viewConfig: {
id: 'gv',
trackOver: false,
stripeRows: false
},
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
items: [{
itemId: 'Button',
text:'显示所选',
tooltip:'Add a new row',
iconCls:'add',
handler:function(){
var record = grid.getSelectionModel().getSelection();
if(record.length == 0){
Ext.MessageBox.show({
title:"提示",
msg:"请先选择您要操作的行!"
//icon: Ext.MessageBox.INFO
})
return;
}else{
var ids = "";
for(var i = 0; i < record.length; i++){
ids += record[i].get("id")
if(i<record.length-1){
ids = ids + ",";
}
}
Ext.MessageBox.show({
title:"所选ID列表",
msg:ids
//icon: Ext.MessageBox.INFO
})
}
}
},'-',{
width: 300,
fieldLabel: '搜索',
labelWidth: 50,
xtype: 'searchfield',
store: store
}]
}, {
dock: 'bottom',
xtype: 'pagingtoolbar',
store: store,
pageSize: 25,
displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
emptyMsg: '没有数据'
}]
})
store.loadPage(1);
})
服务端selectgrid.asp:
1. <%
2. Response.ContentType = "text/html"
3. Response.Charset = "UTF-8"
4. %>
5. <%
6. '返回JSON数据,自定义一些测试数据。。
7. '这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
8. '由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
9. start = Request("start")
10. limit = Request("limit")
11. '查询时获取的参数。
12. query = Request("query")
13. If start = "" Then
14. start = 0
15. End If
16. If limit = "" Then
17. limit = 50
18. End If
19. sorts = Replace(Trim(Request.Form("sort")),"'","")
20. dir = Replace(Trim(Request.Form("dir")),"'","")
21.
22. '测试数据,这里直接输出结果,实际应用中,应该把查询条件放到SQL语句中。
23. If query = "newstitle" Then
24. Echo("{")
25. Echo("""total"":")
26. Echo("""1")
27. Echo(""",""items"":[")
28. Echo("{")
29. Echo("""title"":""newstitle""")
30. Echo(",")
31. Echo("""author"":""author""")
32. Echo(",")
33. Echo("""hits"":""100""")
34. Echo(",")
35. Echo("""addtime"":"""&Now()&"""")
36. Echo("}")
37. Echo("]}")
38. Else
39. Dim counts:counts=300
40. '注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
41.
42. Dim Ls:Ls = Cint(start) + Cint(limit)
43. If Ls >= counts Then
44. Ls = counts
45. End If
46.
47. Echo("{")
48. Echo("""total"":")
49. Echo(""""&counts&""",")
50. Echo("""items"":[")
51. For i = start+1 To Ls
52. Echo("{")
53. Echo("""id"":"""&i&"""")
54. Echo(",")
55. Echo("""title"":""newstitle"&i&"""")
56. Echo(",")
57. Echo("""author"":""author"&i&"""")
58. Echo(",")
59. Echo("""hits"":"""&i&"""")
60. Echo(",")
61. Echo("""addtime"":"""&Now()&"""")
62. Echo("}")
63. If i<Ls Then
64. Echo(",")
65. End If
66. Next
67.
68. Echo("]}")
69. End If
70. Function Echo(str)
71. Response.Write(str)
72. End Function
73. %>
<%
Response.ContentType = "text/html"
Response.Charset = "UTF-8"
%>
<%
'返回JSON数据,自定义一些测试数据。。
'这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
'由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
start = Request("start")
limit = Request("limit")
'查询时获取的参数。
query = Request("query")
If start = "" Then
start = 0
End If
If limit = "" Then
limit = 50
End If
sorts = Replace(Trim(Request.Form("sort")),"'","")
dir = Replace(Trim(Request.Form("dir")),"'","")
'测试数据,这里直接输出结果,实际应用中,应该把查询条件放到SQL语句中。
If query = "newstitle" Then
Echo("{")
Echo("""total"":")
Echo("""1")
Echo(""",""items"":[")
Echo("{")
Echo("""title"":""newstitle""")
Echo(",")
Echo("""author"":""author""")
Echo(",")
Echo("""hits"":""100""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
Echo("]}")
Else
Dim counts:counts=300
'注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
Dim Ls:Ls = Cint(start) + Cint(limit)
If Ls >= counts Then
Ls = counts
End If
Echo("{")
Echo("""total"":")
Echo(""""&counts&""",")
Echo("""items"":[")
For i = start+1 To Ls
Echo("{")
Echo("""id"":"""&i&"""")
Echo(",")
Echo("""title"":""newstitle"&i&"""")
Echo(",")
Echo("""author"":""author"&i&"""")
Echo(",")
Echo("""hits"":"""&i&"""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
If i<Ls Then
Echo(",")
End If
Next
Echo("]}")
End If
Function Echo(str)
Response.Write(str)
End Function
%>
4.9 可编辑的grid
实际运用中,我们可能需要动态修改Grid中的数据,也就是要实现EditGrid功能。本文介绍如何实现Extjs4 EditGrid功能。先发几张效果图。看图说话,更有说服力哦。
HTML代码:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <title>ExtJS4 EditGrid(可编辑的Grid)-MHZG.NET</title>
6. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
7. <link rel="stylesheet" type="text/css" href="../../examples/ux/css/CheckHeader.css" />
8. <style type="text/css">
9. #search-results a {
10. color: #385F95;
11. font:bold 11px tahoma, arial, helvetica, sans-serif;
12. text-decoration:none;
13. }
14. .add {
15. background-image:url(../../examples/shared/icons/fam/cog.gif) !important;
16. }
17. #search-results a:hover {
18. text-decoration:underline;
19. }
20.
21. #search-results p {
22. margin:3px !important;
23. }
24.
25. .search-item {
26. font:normal 11px tahoma, arial, helvetica, sans-serif;
27. padding:3px 10px 3px 10px;
28. border:1px solid #fff;
29. border-bottom:1px solid #eeeeee;
30. white-space:normal;
31. color:#555;
32. }
33. .search-item h3 {
34. display:block;
35. font:inherit;
36. font-weight:bold;
37. color:#222;
38. }
39.
40. .search-item h3 span {
41. float: right;
42. font-weight:normal;
43. margin:0 0 5px 5px;
44. width:100px;
45. display:block;
46. clear:none;
47. }
48.
49. .x-form-clear-trigger {
50. background-image: url(../../resources/themes/images/default/form/clear-trigger.gif);
51. }
52.
53. .x-form-search-trigger {
54. background-image: url(../../resources/themes/images/default/form/search-trigger.gif);
55. }
56. </style>
57. <script type="text/javascript" src="../../bootstrap.js"></script>
58. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
59. <script type="text/javascript" src="editgrid.js"></script>
60. </head>
61.
62. <body>
63. <div id="demo"></div>
64. </body>
65. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ExtJS4 EditGrid(可编辑的Grid)-MHZG.NET</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="../../examples/ux/css/CheckHeader.css" />
<style type="text/css">
#search-results a {
color: #385F95;
font:bold 11px tahoma, arial, helvetica, sans-serif;
text-decoration:none;
}
.add {
background-image:url(../../examples/shared/icons/fam/cog.gif) !important;
}
#search-results a:hover {
text-decoration:underline;
}
#search-results p {
margin:3px !important;
}
.search-item {
font:normal 11px tahoma, arial, helvetica, sans-serif;
padding:3px 10px 3px 10px;
border:1px solid #fff;
border-bottom:1px solid #eeeeee;
white-space:normal;
color:#555;
}
.search-item h3 {
display:block;
font:inherit;
font-weight:bold;
color:#222;
}
.search-item h3 span {
float: right;
font-weight:normal;
margin:0 0 5px 5px;
width:100px;
display:block;
clear:none;
}
.x-form-clear-trigger {
background-image: url(../../resources/themes/images/default/form/clear-trigger.gif);
}
.x-form-search-trigger {
background-image: url(../../resources/themes/images/default/form/search-trigger.gif);
}
</style>
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="editgrid.js"></script>
</head>
<body>
<div id="demo"></div>
</body>
</html>
editgrid.js:
1. Ext.Loader.setConfig({enabled: true});
2. Ext.Loader.setPath('Ext.ux', '../../examples/ux');
3. Ext.require([
4. 'Ext.grid.*',
5. 'Ext.toolbar.Paging',
6. 'Ext.util.*',
7. 'Ext.data.*',
8. 'Ext.state.*',
9. 'Ext.form.*',
10. 'Ext.ux.form.SearchField',
11. 'Ext.selection.CellModel',
12. 'Ext.ux.CheckColumn',
13. 'Ext.selection.CheckboxModel'
14. ]);
15.
16. Ext.onReady(function(){
17. var isEdit = false;
18.
19. function formatDate(value){
20. return value ? Ext.Date.dateFormat(value, 'Y-m-d') : '';
21. }
22.
23. Ext.define('MyData',{
24. extend: 'Ext.data.Model',
25. fields: [
26. {name:'id'},
27. {name:'title',type:'string'},
28. {name:'author'},
29. {name:'hits',type: 'int'},
30. {name:'addtime',type:'date',dataFormat:'Y-m-d'},
31. {name:'checked',type:'bool'}
32. ]
33. });
34.
35. //创建数据源
36. var store = Ext.create('Ext.data.Store', {
37. //分页大小
38. pageSize: 50,
39. model: 'MyData',
40. //是否在服务端排序
41. remoteSort: true,
42. autoDestroy: true,
43. proxy: {
44. //异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
45. type: 'ajax',
46. url: 'editgrid.asp',
47.
48. reader: {
49. root: 'items',
50. totalProperty : 'total'
51. },
52. simpleSortMode: true
53. },
54. sorters: [{
55. //排序字段。
56. property: 'hits',
57. //排序类型,默认为 ASC
58. direction: 'DESC'
59. }]
60. });
61. //下拉框数据,测试数据。
62. var cbstore = Ext.create('Ext.data.Store', {
63. fields: ['id', 'name'],
64. data : [
65. {"id":"1","name":"佚名"},
66. {"id":"2","name":"管理员"},
67. {"id":"3","name":"编辑"},
68. {"id":"4","name":"总编辑"},
69. {"id":"5","name":"测试员"}
70. ]
71. });
72.
73.
74. //创建多选
75. var selModel = Ext.create('Ext.selection.CheckboxModel');
76. var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
77. clicksToEdit: 1
78. });
79. //创建Grid
80. var grid = Ext.create('Ext.grid.Panel',{
81. store: store,
82. selModel: selModel,
83. columnLines: true,
84. columns: [{
85. id:"title",
86. header: "标题",
87. width: 110,
88. dataIndex: 'title',
89. sortable: true,
90. field: {
91. allowBlank: false
92. }
93. },{
94. header: "作者",
95. width: 120,
96. dataIndex: 'author',
97. id:'gc',
98. sortable: false,
99. field: {
100. xtype: 'combobox',
101. id:'authors',
102. typeAhead: true,
103. triggerAction: 'all',
104. queryMode: 'local',
105. selectOnTab: true,
106. store: cbstore,
107. lazyRender: true,
108. displayField:'name',
109. valueField:'id',
110. listClass: 'x-combo-list-small',
111. listeners:{
112. select : function(combo, record,index){
113. isEdit = true;
114. }
115. }
116.
117. },
118. renderer:rendererData
119. },{
120. header: "点击数",
121. width: 80,
122. dataIndex: 'hits',
123. sortable: true,
124. field: {
125. xtype: 'numberfield',
126. allowBlank: false,
127. minValue: 0,
128. maxValue: 100000
129. }
130. },{
131. header: "添加时间",
132. width: 100,
133. dataIndex: 'addtime',
134. sortable: true,
135. renderer: formatDate,
136. field: {
137. xtype: 'datefield',
138. format: 'y-m-d',
139. minValue: '01/01/06'
140. }
141. },{
142. xtype: 'checkcolumn',
143. header:'审核',
144. dataIndex:'checked',
145. width:55
146. }],
147. height:400,
148. width:520,
149. x:20,
150. y:40,
151. title: 'ExtJS4 EditGrid(可编辑的Grid)',
152.
153. disableSelection: false,//值为TRUE,表示禁止选择
154. frame: true,
155. selType: 'cellmodel',
156. loadMask: true,
157. renderTo: 'demo',
158. viewConfig: {
159. id: 'gv',
160. trackOver: false,
161. stripeRows: false
162. },
163. dockedItems: [{
164. dock: 'top',
165. xtype: 'toolbar',
166. items: [{
167. itemId: 'Button',
168. text:'显示所选',
169. tooltip:'Add a new row',
170. iconCls:'add',
171. handler:function(){
172. var record = grid.getSelectionModel().getSelection();
173. if(record.length == 0){
174. Ext.MessageBox.show({
175. title:"提示",
176. msg:"请先选择您要操作的行!",
177. icon: Ext.MessageBox.INFO,
178. buttons: Ext.Msg.OK
179. })
180. return;
181. }else{
182. var ids = "";
183. for(var i = 0; i < record.length; i++){
184. ids += record[i].get("id")
185. if(i<record.length-1){
186. ids = ids + ",";
187. }
188. }
189. Ext.MessageBox.show({
190. title:"所选ID列表",
191. msg:ids
192. //icon: Ext.MessageBox.INFO
193. })
194. }
195. }
196. },'-',{
197. width: 300,
198. fieldLabel: '搜索',
199. labelWidth: 50,
200. xtype: 'searchfield',
201. store: store
202. }]
203. }, {
204. dock: 'bottom',
205. xtype: 'pagingtoolbar',
206. store: store,
207. pageSize: 25,
208. displayInfo: true,
209. displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
210. emptyMsg: '没有数据'
211. }],
212. plugins: [cellEditing]
213.
214. })
215. store.loadPage(1);
216. grid.on('edit', onEdit, this);
217.
218. function onEdit(){
219. var record = grid.getSelectionModel().getSelection()[0];
220. //这里进行异步操作,关于Extjs的异步操作这里不做演示,仅列出所有值。
221. var title = record.get('title');
222. var author = record.get('author');//注意,这里得到的是id值,而不是name值,如果没有修改作者,那么得到的值是默认显示的字符串,这个需要在服务端进行判断并处理。
223. var clk = record.get('hits');
224. var addtime = Ext.Date.dateFormat(record.get('addtime'), 'Y-m-d');
225. var checked = record.get('checked');
226. Ext.MessageBox.show({
227. title:"修改的数据为",
228. msg:title+"\r\n"+author+"\r\n"+clk+"\r\n"+addtime+"\r\n"+checked,
229. icon: Ext.MessageBox.INFO,
230. buttons: Ext.Msg.OK
231. })
232. }
233.
234. function rendererData(value,metadata,record){
235. if(isEdit){
236. var index = cbstore.find(Ext.getCmp('authors').valueField,value);
237. var record = cbstore.getAt(index);
238. return record.data.name;
239. }else{
240. return value;
241. }
242.
243. }
244. })
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', '../../examples/ux');
Ext.require([
'Ext.grid.*',
'Ext.toolbar.Paging',
'Ext.util.*',
'Ext.data.*',
'Ext.state.*',
'Ext.form.*',
'Ext.ux.form.SearchField',
'Ext.selection.CellModel',
'Ext.ux.CheckColumn',
'Ext.selection.CheckboxModel'
]);
Ext.onReady(function(){
var isEdit = false;
function formatDate(value){
return value ? Ext.Date.dateFormat(value, 'Y-m-d') : '';
}
Ext.define('MyData',{
extend: 'Ext.data.Model',
fields: [
{name:'id'},
{name:'title',type:'string'},
{name:'author'},
{name:'hits',type: 'int'},
{name:'addtime',type:'date',dataFormat:'Y-m-d'},
{name:'checked',type:'bool'}
]
});
//创建数据源
var store = Ext.create('Ext.data.Store', {
//分页大小
pageSize: 50,
model: 'MyData',
//是否在服务端排序
remoteSort: true,
autoDestroy: true,
proxy: {
//异步获取数据,这里的URL可以改为任何动态页面,只要返回JSON数据即可
type: 'ajax',
url: 'editgrid.asp',
reader: {
root: 'items',
totalProperty : 'total'
},
simpleSortMode: true
},
sorters: [{
//排序字段。
property: 'hits',
//排序类型,默认为 ASC
direction: 'DESC'
}]
});
//下拉框数据,测试数据。
var cbstore = Ext.create('Ext.data.Store', {
fields: ['id', 'name'],
data : [
{"id":"1","name":"佚名"},
{"id":"2","name":"管理员"},
{"id":"3","name":"编辑"},
{"id":"4","name":"总编辑"},
{"id":"5","name":"测试员"}
]
});
//创建多选
var selModel = Ext.create('Ext.selection.CheckboxModel');
var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
});
//创建Grid
var grid = Ext.create('Ext.grid.Panel',{
store: store,
selModel: selModel,
columnLines: true,
columns: [{
id:"title",
header: "标题",
width: 110,
dataIndex: 'title',
sortable: true,
field: {
allowBlank: false
}
},{
header: "作者",
width: 120,
dataIndex: 'author',
id:'gc',
sortable: false,
field: {
xtype: 'combobox',
id:'authors',
typeAhead: true,
triggerAction: 'all',
queryMode: 'local',
selectOnTab: true,
store: cbstore,
lazyRender: true,
displayField:'name',
valueField:'id',
listClass: 'x-combo-list-small',
listeners:{
select : function(combo, record,index){
isEdit = true;
}
}
},
renderer:rendererData
},{
header: "点击数",
width: 80,
dataIndex: 'hits',
sortable: true,
field: {
xtype: 'numberfield',
allowBlank: false,
minValue: 0,
maxValue: 100000
}
},{
header: "添加时间",
width: 100,
dataIndex: 'addtime',
sortable: true,
renderer: formatDate,
field: {
xtype: 'datefield',
format: 'y-m-d',
minValue: '01/01/06'
}
},{
xtype: 'checkcolumn',
header:'审核',
dataIndex:'checked',
width:55
}],
height:400,
width:520,
x:20,
y:40,
title: 'ExtJS4 EditGrid(可编辑的Grid)',
disableSelection: false,//值为TRUE,表示禁止选择
frame: true,
selType: 'cellmodel',
loadMask: true,
renderTo: 'demo',
viewConfig: {
id: 'gv',
trackOver: false,
stripeRows: false
},
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
items: [{
itemId: 'Button',
text:'显示所选',
tooltip:'Add a new row',
iconCls:'add',
handler:function(){
var record = grid.getSelectionModel().getSelection();
if(record.length == 0){
Ext.MessageBox.show({
title:"提示",
msg:"请先选择您要操作的行!",
icon: Ext.MessageBox.INFO,
buttons: Ext.Msg.OK
})
return;
}else{
var ids = "";
for(var i = 0; i < record.length; i++){
ids += record[i].get("id")
if(i<record.length-1){
ids = ids + ",";
}
}
Ext.MessageBox.show({
title:"所选ID列表",
msg:ids
//icon: Ext.MessageBox.INFO
})
}
}
},'-',{
width: 300,
fieldLabel: '搜索',
labelWidth: 50,
xtype: 'searchfield',
store: store
}]
}, {
dock: 'bottom',
xtype: 'pagingtoolbar',
store: store,
pageSize: 25,
displayInfo: true,
displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
emptyMsg: '没有数据'
}],
plugins: [cellEditing]
})
store.loadPage(1);
grid.on('edit', onEdit, this);
function onEdit(){
var record = grid.getSelectionModel().getSelection()[0];
//这里进行异步操作,关于Extjs的异步操作这里不做演示,仅列出所有值。
var title = record.get('title');
var author = record.get('author');//注意,这里得到的是id值,而不是name值,如果没有修改作者,那么得到的值是默认显示的字符串,这个需要在服务端进行判断并处理。
var clk = record.get('hits');
var addtime = Ext.Date.dateFormat(record.get('addtime'), 'Y-m-d');
var checked = record.get('checked');
Ext.MessageBox.show({
title:"修改的数据为",
msg:title+"\r\n"+author+"\r\n"+clk+"\r\n"+addtime+"\r\n"+checked,
icon: Ext.MessageBox.INFO,
buttons: Ext.Msg.OK
})
}
function rendererData(value,metadata,record){
if(isEdit){
var index = cbstore.find(Ext.getCmp('authors').valueField,value);
var record = cbstore.getAt(index);
return record.data.name;
}else{
return value;
}
}
})
editgrid.asp:时间关系,只简单做了些测试数据。
1. <%
2. Response.ContentType = "text/html"
3. Response.Charset = "UTF-8"
4. %>
5. <%
6. '返回JSON数据,自定义一些测试数据。。
7. '这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
8. '由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
9. start = Request("start")
10. limit = Request("limit")
11. '查询时获取的参数。
12. query = Request("query")
13. If start = "" Then
14. start = 0
15. End If
16. If limit = "" Then
17. limit = 50
18. End If
19. sorts = Replace(Trim(Request.Form("sort")),"'","")
20. dir = Replace(Trim(Request.Form("dir")),"'","")
21.
22. '测试数据,这里直接输出结果,实际应用中,应该把查询条件放到SQL语句中。
23. If query = "newstitle" Then
24. Echo("{")
25. Echo("""total"":")
26. Echo("""1")
27. Echo(""",""items"":[")
28. Echo("{")
29. Echo("""title"":""newstitle""")
30. Echo(",")
31. Echo("""author"":""author""")
32. Echo(",")
33. Echo("""hits"":""100""")
34. Echo(",")
35. Echo("""addtime"":"""&Now()&"""")
36. Echo("}")
37. Echo("]}")
38. Else
39. Dim counts:counts=20
40. '注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
41.
42. Dim Ls:Ls = Cint(start) + Cint(limit)
43. If Ls >= counts Then
44. Ls = counts
45. End If
46.
47. Echo("{")
48. Echo("""total"":")
49. Echo(""""&counts&""",")
50. Echo("""items"":[")
51. For i = start+1 To Ls
52. Echo("{")
53. Echo("""id"":"""&i&"""")
54. Echo(",")
55. Echo("""title"":""newstitle"&i&"""")
56. Echo(",")
57. Echo("""author"":""author"&i&"""")
58. Echo(",")
59. Echo("""hits"":"""&i&"""")
60. Echo(",")
61. Echo("""addtime"":"""&Now()&"""")
62. Echo(",")
63. If i Mod 4 = 0 Then
64. Echo("""checked"":""true""")
65. Else
66. Echo("""checked"":""false""")
67. End If
68. Echo("}")
69. If i<Ls Then
70. Echo(",")
71. End If
72. Next
73.
74. Echo("]}")
75. End If
76. Function Echo(str)
77. Response.Write(str)
78. End Function
79. %>
<%
Response.ContentType = "text/html"
Response.Charset = "UTF-8"
%>
<%
'返回JSON数据,自定义一些测试数据。。
'这里的参数与EXT3.x相同,区别在于排序字段和排序方式使用了新的属性。
'由于这里是测试数据,接收的参数只用到了start,limit。sorts和dir在实际操作过程中,将之加入SQL的ORDER BY里即可。
start = Request("start")
limit = Request("limit")
'查询时获取的参数。
query = Request("query")
If start = "" Then
start = 0
End If
If limit = "" Then
limit = 50
End If
sorts = Replace(Trim(Request.Form("sort")),"'","")
dir = Replace(Trim(Request.Form("dir")),"'","")
'测试数据,这里直接输出结果,实际应用中,应该把查询条件放到SQL语句中。
If query = "newstitle" Then
Echo("{")
Echo("""total"":")
Echo("""1")
Echo(""",""items"":[")
Echo("{")
Echo("""title"":""newstitle""")
Echo(",")
Echo("""author"":""author""")
Echo(",")
Echo("""hits"":""100""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo("}")
Echo("]}")
Else
Dim counts:counts=20
'注意,这里的counts相当于Rs.RecordCount,也就是记录总数。
Dim Ls:Ls = Cint(start) + Cint(limit)
If Ls >= counts Then
Ls = counts
End If
Echo("{")
Echo("""total"":")
Echo(""""&counts&""",")
Echo("""items"":[")
For i = start+1 To Ls
Echo("{")
Echo("""id"":"""&i&"""")
Echo(",")
Echo("""title"":""newstitle"&i&"""")
Echo(",")
Echo("""author"":""author"&i&"""")
Echo(",")
Echo("""hits"":"""&i&"""")
Echo(",")
Echo("""addtime"":"""&Now()&"""")
Echo(",")
If i Mod 4 = 0 Then
Echo("""checked"":""true""")
Else
Echo("""checked"":""false""")
End If
Echo("}")
If i<Ls Then
Echo(",")
End If
Next
Echo("]}")
End If
Function Echo(str)
Response.Write(str)
End Function
%>
由于4.x有了重大更新,所以很多在3.x中的东西在4.x里并不好使,直接导致了本文迟迟没有发表,原因就出在了更新下拉框后,Grid中显示的是ID号而不是name值。一番查看API及研究,终于发现问题所在,3.x中renderer combobox在4.x并不好用,最后重新读了一遍自己写的代码,才有所顿悟。希望文章能起到一个抛砖引玉的作用,大家一起进步。
4.10 图片验证码的实现
上几篇文章,主要学习了Extjs4 Grid的使用方法,从本篇开始,我们开始其他组件的学习,使用。在登录、注册甚至是发表文章或帖子的时候,都会用到验证码这个东西,那么在EXTJS中,可以使用验证码功能么?答案是肯定的,在EXTJS4之前,也有很多验证码的实现,在Extjs4中,验证码到底如何实现呢?
暂时,我们将验证码组件,命名为CheckCode。此组件继承自Ext.form.field.Text,在实现之前,我们需要写两个样式,分别用来控制验证码的输入框和验证码图片的大小。
CSS样式为:
1. #CheckCode{ float:left;}
2. .x-form-code{width:73px;height:20px;vertical-align:middle;cursor:pointer; float:left; margin-left:7px;}
#CheckCode{ float:left;}
.x-form-code{width:73px;height:20px;vertical-align:middle;cursor:pointer; float:left; margin-left:7px;}
记住这两个样式的定义,后面,我们会用到它。
验证码的JS代码:
1. Ext.define('SMS.view.CheckCode',{
2. extend: 'Ext.form.field.Text',
3. alias: 'widget.checkcode',
4. inputTyle:'codefield',
5. codeUrl:Ext.BLANK_IMAGE_URL,
6. isLoader:true,
7. onRender:function(ct,position){
8. this.callParent(arguments);
9. this.codeEl = ct.createChild({ tag: 'img', src: Ext.BLANK_IMAGE_URL});
10. this.codeEl.addCls('x-form-code');
11. this.codeEl.on('click', this.loadCodeImg, this);
12.
13. if (this.isLoader) this.loadCodeImg();
14. },
15. alignErrorIcon: function() {
16. this.errorIcon.alignTo(this.codeEl, 'tl-tr', [2, 0]);
17. },
18.
19. loadCodeImg: function() {
20. this.codeEl.set({ src: this.codeUrl + '?id=' + Math.random() });
21. }
22.
23. })
Ext.define('SMS.view.CheckCode',{
extend: 'Ext.form.field.Text',
alias: 'widget.checkcode',
inputTyle:'codefield',
codeUrl:Ext.BLANK_IMAGE_URL,
isLoader:true,
onRender:function(ct,position){
this.callParent(arguments);
this.codeEl = ct.createChild({ tag: 'img', src: Ext.BLANK_IMAGE_URL});
this.codeEl.addCls('x-form-code');
this.codeEl.on('click', this.loadCodeImg, this);
if (this.isLoader) this.loadCodeImg();
},
alignErrorIcon: function() {
this.errorIcon.alignTo(this.codeEl, 'tl-tr', [2, 0]);
},
loadCodeImg: function() {
this.codeEl.set({ src: this.codeUrl + '?id=' + Math.random() });
}
})
以上代码中,定义了一个“类”,名字是:SMS.view.CheckCode,其实这个名字,相当于extjs 3.x之中的命名空间,以前也提到过。它继承自Ext.form.field.Text,在它的onRender中,我们写了一些代码。其中this.callParent(arguments); 代替了xxxx.superclass.onRender.call(this, ct, position);在Ext.form.field.Text的基础上,使用createChild方法,创建了一个图片,并为其添加了一个名为x-form-code,而后,给其创建了一个click事件,这个事件实现的功能是,当我们点击验证码图片时,换另外一张图片,也就是常说的:“看不清?换一张功能。”,最后,如果isLoader为True时,调用loadCodeImg方法。至此,验证码功能全部完成了。下面,我们看看如何使用。
新建Login.js文件,定义“类”SMS.view.Login,其全部代码为:
1. Ext.define('SMS.view.Login',{
2. extend:'Ext.window.Window',
3. alias: 'widget.loginForm',
4. requires: ['Ext.form.*','SMS.view.CheckCode'],
5. initComponent:function(){
6. var checkcode = Ext.create('SMS.view.CheckCode',{
7. cls : 'key',
8. fieldLabel : '验证码',
9. name : 'CheckCode',
10. id : 'CheckCode',
11. allowBlank : false,
12. isLoader:true,
13. blankText : '验证码不能为空',
14. codeUrl: '/include/checkCode.asp',
15. width : 160
16. })
17. var form = Ext.widget('form',{
18. border: false,
19. bodyPadding: 10,
20. fieldDefaults: {
21. labelAlign: 'left',
22. labelWidth: 55,
23. labelStyle: 'font-weight:bold'
24. },
25. defaults: {
26. margins: '0 0 10 0'
27. },
28. items:[{
29. xtype: 'textfield',
30. fieldLabel: '用户名',
31. blankText : '用户名不能为空',
32. allowBlank: false,
33. width:240
34. },{
35. xtype: 'textfield',
36. fieldLabel: '密 码',
37. allowBlank: false,
38. blankText : '密码不能为空',
39. width:240,
40. inputType : 'password'
41. },checkcode],
42. buttons:[{
43. text:'登录',
44. handler:function(){
45.
46. }
47. },{
48. text:'取消',
49. handler:function(){
50.
51. }
52. }]
53. })
54. Ext.apply(this,{
55. height: 160,
56. width: 280,
57. title: '用户登陆',
58. closeAction: 'hide',
59. closable : false,
60. iconCls: 'login',
61. layout: 'fit',
62. modal : true,
63. plain : true,
64. resizable: false,
65. items:form
66. });
67. this.callParent(arguments);
68. }
69. });
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 : '用户名不能为空',
allowBlank: false,
width:240
},{
xtype: 'textfield',
fieldLabel: '密 码',
allowBlank: false,
blankText : '密码不能为空',
width:240,
inputType : 'password'
},checkcode],
buttons:[{
text:'登录',
handler:function(){
}
},{
text:'取消',
handler:function(){
}
}]
})
Ext.apply(this,{
height: 160,
width: 280,
title: '用户登陆',
closeAction: 'hide',
closable : false,
iconCls: 'login',
layout: 'fit',
modal : true,
plain : true,
resizable: false,
items:form
});
this.callParent(arguments);
}
});
然后在主页面的代码中调用此LoginWindow。
1. requires:['SMS.view.Login']
2. var win;
3. win = Ext.create('SMS.view.Login').show();
requires:['SMS.view.Login']
var win;
win = Ext.create('SMS.view.Login').show();
最后效果图:
4.11 tabpanel
创建一个tabPanel有两种方法:
一:Ext.createWidget('tabpanel',{...})
二:Ext.create('Ext.tab.Panel',{...})
本文分别介绍这两种创建方法。
HTML代码:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
6. <script type="text/javascript" src="../../bootstrap.js"></script>
7. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
8. <script type="text/javascript" src="tabs.js"></script>
9. <title>MHZG.NET--Tabs</title>
10. </head>
11.
12. <body>
13. <div id="tab"></div>
14. </body>
15. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="tabs.js"></script>
<title>MHZG.NET--Tabs</title>
</head>
<body>
<div id="tab"></div>
</body>
</html>
tabs.js:
1. Ext.require('Ext.tab.*');
2. Ext.onReady(function(){
3. //第一种方式创建
4. var tabs = Ext.createWidget('tabpanel', {
5. renderTo: 'tab',
6. width: 450,
7. activeTab: 0,
8. margin:'50 10 50 80',
9. defaults :{
10. bodyPadding: 10
11. },
12. items: [{
13. //contentEl:'script',//将指定容器中的内容加载到tabPanel的内容区
14. title: 'Tabs-1',
15. closable: true,
16. html:'Tabs-1内容。'
17. },{
18. title: 'Tabs-2',
19. closable: false,
20. html:'Tabs-2内容'
21. }]
22. });
23.
24. //第二种方式创建
25. var tabs2 = Ext.create('Ext.tab.Panel',{
26. renderTo: document.body,
27. activeTab: 0,
28. width: 600,
29. height: 250,
30. plain: true,
31. margin:'0 10 0 80',
32. defaults :{
33. autoScroll: true,
34. bodyPadding: 10
35. },
36. items: [{
37. title: 'Tabs-1',
38. html: "这里显示内容"
39. },{
40. title: '异步加载内容',
41. loader: {
42. url: 'ajax.htm',
43. contentType: 'html',
44. loadMask: true
45. },
46. listeners: {
47. activate: function(tab) {
48. tab.loader.load();
49. }
50. }
51. },{
52. title: '异步加载内容1',
53. loader: {
54. url: 'ajax1.htm',
55. contentType: 'html',
56. autoLoad: true,
57. params: 'foo=123&bar=abc'
58. }
59. },{
60. title: '点击触发事件',
61. listeners: {
62. activate: function(tab){
63. alert(tab.title);
64. }
65. },
66. html: "点击Tab之后,触发事件,监听事件:activate。activate可传递两个参数。1、Ext.Component this。2、Object options "
67. },{
68. title: 'Tabs不可能',
69. disabled: true
70. }
71. ]
72. })
73. });
Ext.require('Ext.tab.*');
Ext.onReady(function(){
//第一种方式创建
var tabs = Ext.createWidget('tabpanel', {
renderTo: 'tab',
width: 450,
activeTab: 0,
margin:'50 10 50 80',
defaults :{
bodyPadding: 10
},
items: [{
//contentEl:'script',//将指定容器中的内容加载到tabPanel的内容区
title: 'Tabs-1',
closable: true,
html:'Tabs-1内容。'
},{
title: 'Tabs-2',
closable: false,
html:'Tabs-2内容'
}]
});
//第二种方式创建
var tabs2 = Ext.create('Ext.tab.Panel',{
renderTo: document.body,
activeTab: 0,
width: 600,
height: 250,
plain: true,
margin:'0 10 0 80',
defaults :{
autoScroll: true,
bodyPadding: 10
},
items: [{
title: 'Tabs-1',
html: "这里显示内容"
},{
title: '异步加载内容',
loader: {
url: 'ajax.htm',
contentType: 'html',
loadMask: true
},
listeners: {
activate: function(tab) {
tab.loader.load();
}
}
},{
title: '异步加载内容1',
loader: {
url: 'ajax1.htm',
contentType: 'html',
autoLoad: true,
params: 'foo=123&bar=abc'
}
},{
title: '点击触发事件',
listeners: {
activate: function(tab){
alert(tab.title);
}
},
html: "点击Tab之后,触发事件,监听事件:activate。activate可传递两个参数。1、Ext.Component this。2、Object options "
},{
title: 'Tabs不可能',
disabled: true
}
]
})
});
第二个tabPanel中有两个html文件,分别是ajax.htm和ajax1.htm,这两个文件代码就不写了,里面就是敲了一些字,而这些字就是tabPanel内容区的那些文字,不过需要注意的一点就是,在异步获取其他文件中的内容时,这些文件返回的编码格式应该是UTF-8...
4.12 选项卡(tabs)
tab选项卡,是Extjs中常用的组件,tab一般依附于tabpanel,很多时候我也认为tab就是tabpanel,但在官方api中,确实有Ext.tab.Panel和Ext.tab.Tab之分,具体属性、事件、方法,请参考API,tab选项卡可以单独渲染,使用方法是:xtype: 'tab',本文介绍tab的基本用法。
HTML代码:除了加载基本库以外,定义了一些CSS样式,这些CSS基本没用,就是我看着所有组件靠着BODY太不舒服了。。
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
6. <script type="text/javascript" src="../../bootstrap.js"></script>
7. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
8. <script type="text/javascript" src="tabs1.js"></script>
9. <title>MHZG.NET--Tabs</title>
10. <style>
11. .main{ margin:50px auto;}
12. #add{ padding-left:10px;}
13. </style>
14. </head>
15.
16. <body>
17. <div class="main">
18. <div id="add"></div>
19. <div id="tab"></div>
20. </div>
21. </body>
22. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="tabs1.js"></script>
<title>MHZG.NET--Tabs</title>
<style>
.main{ margin:50px auto;}
#add{ padding-left:10px;}
</style>
</head>
<body>
<div class="main">
<div id="add"></div>
<div id="tab"></div>
</div>
</body>
</html>
tabs1.js:
1. Ext.require('Ext.tab.*');
2. Ext.onReady(function(){
3. var currentItem;
4. var tabs = Ext.createWidget('tabpanel', {
5. renderTo: 'tab',
6. resizeTabs: true,
7. enableTabScroll: true,
8. margin:'10',
9. width: 600,
10. height: 250,
11. defaults: {
12. autoScroll:true,
13. bodyPadding: 10
14. },
15. items: [{
16. title: '选项卡',
17. html: '选项卡内容',
18. closable: true
19. }]
20.
21. });
22.
23. var index = 0;
24.
25. function addTab (closable) {
26. ++index;
27. tabs.add({
28. title: '新选项卡- ' + index,
29. html: '新选项卡内容 ' + index + '<br/><br/>',
30. closable: !!closable
31. }).show();
32. }
33.
34. Ext.createWidget('button', {
35. renderTo: 'add',
36. text: '创建可关闭选项卡',
37. handler: function () {
38. addTab(true);
39. }
40. });
41.
42. Ext.createWidget('button', {
43. renderTo: 'add',
44. text: '创建不可关闭选项卡',
45. handler: function () {
46. addTab(false);
47. },
48. style: 'margin-left: 8px;'
49. });
50. });
Ext.require('Ext.tab.*');
Ext.onReady(function(){
var currentItem;
var tabs = Ext.createWidget('tabpanel', {
renderTo: 'tab',
resizeTabs: true,
enableTabScroll: true,
margin:'10',
width: 600,
height: 250,
defaults: {
autoScroll:true,
bodyPadding: 10
},
items: [{
title: '选项卡',
html: '选项卡内容',
closable: true
}]
});
var index = 0;
function addTab (closable) {
++index;
tabs.add({
title: '新选项卡- ' + index,
html: '新选项卡内容 ' + index + '<br/><br/>',
closable: !!closable
}).show();
}
Ext.createWidget('button', {
renderTo: 'add',
text: '创建可关闭选项卡',
handler: function () {
addTab(true);
}
});
Ext.createWidget('button', {
renderTo: 'add',
text: '创建不可关闭选项卡',
handler: function () {
addTab(false);
},
style: 'margin-left: 8px;'
});
});
4.13 上传文件
本文介绍Extjs4文件上传示例,Extjs4中,主要使用up('form').getForm().submit()方法来实现文件的上传,在submit方法中,指定其type属性,这点很重要,如果不指定,那么在上传完成后的处理中,除非服务端不返回数据,否则客户端就会报错。实例代码如下:
upload.html:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
6. <script type="text/javascript" src="../../bootstrap.js"></script>
7. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
8. <script type="text/javascript" src="upload.js"></script>
9. <title>MHZG.NET--upload</title>
10. </head>
11.
12. <body>
13. <div id="upload"></div>
14. </body>
15. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="upload.js"></script>
<title>MHZG.NET--upload</title>
</head>
<body>
<div id="upload"></div>
</body>
</html>
upload.js:
1. Ext.onReady(function(){
2. Ext.create('Ext.form.Panel', {
3. title: '文件上传示例--MHZG.NET',
4. width: 400,
5. bodyPadding: 10,
6. margin:'50 10 50 80',
7. frame: true,
8. renderTo: 'upload',
9. items: [{
10. xtype: 'filefield',
11. name: 'fileName',
12. fieldLabel: '上传',
13. labelWidth: 50,
14. msgTarget: 'side',
15. allowBlank: false,
16. anchor: '100%',
17. buttonText: '选择文件'
18. }],
19.
20. buttons: [{
21. text: '上传',
22. handler: function() {
23. var form = this.up('form').getForm();
24. if(form.isValid()){
25. form.submit({
26. url: 'upload.asp',
27. type:'ajax',
28. waitMsg: '正在保存文件...',
29. success: function(fp, o) {
30. // Ext.Msg.alert('提示信息', '文件成功上传,文件名字为:'+o.result.file);
31. Ext.Msg.show({
32. title:'提示信息',
33. msg:'文件上传成功<br>上传文件名为:'+o.result.file,
34. minWidth:200,
35. modal:true,
36. buttons:Ext.Msg.OK
37. })
38. form.findField('fileName').setRawValue('');
39. }
40. });
41. }
42. }
43. }]
44. });
45. });
Ext.onReady(function(){
Ext.create('Ext.form.Panel', {
title: '文件上传示例--MHZG.NET',
width: 400,
bodyPadding: 10,
margin:'50 10 50 80',
frame: true,
renderTo: 'upload',
items: [{
xtype: 'filefield',
name: 'fileName',
fieldLabel: '上传',
labelWidth: 50,
msgTarget: 'side',
allowBlank: false,
anchor: '100%',
buttonText: '选择文件'
}],
buttons: [{
text: '上传',
handler: function() {
var form = this.up('form').getForm();
if(form.isValid()){
form.submit({
url: 'upload.asp',
type:'ajax',
waitMsg: '正在保存文件...',
success: function(fp, o) {
// Ext.Msg.alert('提示信息', '文件成功上传,文件名字为:'+o.result.file);
Ext.Msg.show({
title:'提示信息',
msg:'文件上传成功<br>上传文件名为:'+o.result.file,
minWidth:200,
modal:true,
buttons:Ext.Msg.OK
})
form.findField('fileName').setRawValue('');
}
});
}
}
}]
});
});
服务端文件upload.asp的内容就不写了,由于在文件中用到了各种无组件上传代码,所以比较杂乱,注意一点就可以,在任何类型的服务端处理文件中,在处理完上传之后,给客户端返回一段JSON,客户端就可以通知客户上传完成。服务端处理完上传之后返回的JSON字符串为如下类型:{success:true,file:"""&fileName&"""},JSON字符串可以任意组合,这里的fileName为上传后文件地址+文件名(/UploadFile/2011830.gif),以便于客户端进行其他操作。
最后,在代码中用到了form.findField('fileName').setRawValue('');,这句是清空file选择框,因为用form.reset();根本无法清除file选择框中的内容,无奈之下用了这个办法,如果大家有更好的办法,欢迎留言指正。。
4.14 ComponentQuery
对于Extjs3.x来说,Extjs4.0组件查找使用了ComponentQuery类,这个类是用来查找容器内的组件的。实现方式类似于CSS的Selector。在extjs3.x中,查找组件的方式有很多,例如:(ID组件ID)、(ref组件引用)、(items.get(0)组件的层级)。在extjs4.0中,则多了ComponentQuery。利用这个类,将更加方便的查找组件。下面,则是将extjs3和extjs4各种查找组件的方式做下对比。
Extjs3.x:
ID:这就是所熟知的Ext.getCmp(“组件ID”),虽然说这种方式查找组件最直接,也最容易,但是如果随着程序体积变大,组件越来越多,那么可能会发生组件ID相同的事情发生,这样一来,就可能发生显示的问题和获取对象不正常。
ref:在EXTJS3中,所有的组件都会有一个ref属性,也就是reference的意思。这种方式是通过对组件的引用而得到组件对象。例如:ref:’mypanel’,但是这种方式的局限性在于,他只能查找不同层级之间的组件。也就是说A[a,b,c] C[d,e,f],这样的方式,使用ref非常方便,但如果是B[c,c,d]这样的方式,就会出现问题,因为在某一层级上,出项了两个相同的组件。
items.get(0):这种方式是通过首先获得组件中元素的一个数组,然后通过数组获得想要的组件对象,这种方式,我们一般不会用到,因为,他非常不灵活,如果我们的元素层级发生改变的话,我们将不能获取我们想要的组件,维护起来也非常困难。所以,这种方式,是用的最少的一种方式。
在Extjs4.0中,我们依然可以沿用Extjs3.x中查找组件的方式,但是在Extjs4.0中,我们可以利用ComponentQuery,方便找到对应组件。
1、 通过组件ID获取组件:"#组件ID”,如果通过这种方式,那么一定要记住在组件ID前添加#号。
2、 得到某一组件下所有的指定类型的组件:"panel>button”,这种方式是查找所有panel组件下的所有button组件。
3、 通过xtype:"treepanel”或".treepanel
4、如果想获取所有button并且action为save的button,则可以使用"button[action=save] ",又或者获取所有panel,并且autoscroll属性为true的panel,则可以使用"panel[autoScroll=true]"
还有两种方式,是查找某一组件的子组件或上级组件,例如:
1、查找window下的form:win.down(“form”)
2、查找button的父组件window:button.up(“window”);
最明显的例子就是我们在extjs4.0使用MVC模式进行开发的时候,经常会在control控制中大量使用'viewport > panel',’edit button[action=save]’这类查找,当我们点击button进行数据保存的时候,我们会使用
1. var win = button.up('window'),
2. form = win.down('form'),
3. record = form.getRecord(),
var win = button.up('window'),
form = win.down('form'),
record = form.getRecord(),
这类型的查找进行数据更新。
最后,在extjs4.0 MVC模式中,经常会碰到
1. refs:[
2. {ref: 'menu',selector: 'tablepanel'},
3. {ref: 'feedList', selector: 'feedlist'},
4. {
5. ref: 'feedWindow',
6. selector: 'feedwindow',
7. autoCreate: true,
8. xtype: 'feedwindow'
9. }
10. ]
refs:[
{ref: 'menu',selector: 'tablepanel'},
{ref: 'feedList', selector: 'feedlist'},
{
ref: 'feedWindow',
selector: 'feedwindow',
autoCreate: true,
xtype: 'feedwindow'
}
]
这样的书写方式。有很多人问我这是什么意思,查API也找不到。网络上也找不到,其实看extjs3.x中查找组件的方式,就会明白了。Refs是一个查找并匹配组件的集合,集合里面则指定了需要的操作,即查找一个组件,而找到的这个组件所匹配组件就是selector指定的,用上面代码一条{ref: 'menu',selector: 'tablepanel'}来稍做解释,即查找menu组件(其实是一颗树),点击树节点,结果将在'tablepanel'中显示。这样解释可能比较含糊,那么就用官方例子做个解释吧。{ref: 'feedData', selector: 'feedlist dataview'}。这条的含义是查找'feedData',将'feedData'显示在'feedlist的子组件dataview'上。
总结下,就是在ExtJS4中所有的组件都有一个query方法,这个方法就是ComponentQuery的实现。当然,如果还是不明白的话,就可以使用:
Ext.ComponentQuery.query("tabpanel");
Ext.ComponentQuery.query("#mytree")
等等。实在extjs4.0实际使用过程中,如果要调用或查找组件,exjts会自动调用ComponentQuery的query方法来查找对应组件。
例子:
1. init:function(){
2. this.control({
3. 'smsmenu': {//这里不必写全部的代码Ext.ComponentQuery.query(…);
4. itemmousedown: this.loadMenu
5. }
6. })
7. }
init:function(){
this.control({
'smsmenu': {//这里不必写全部的代码Ext.ComponentQuery.query(…);
itemmousedown: this.loadMenu
}
})
}
这就是本节所学到的内容,如果文中有错误或有更好的方法,欢迎留言交流。我们一起学习,共同进步!!
4.15 Ext.data.model
extjs4.0中,在data类中新增加了一个Model(模型类),这个类有点类似于extjs3.x中的record。Model类功能非常强大,尤其在extjs4.0 MVC开发中,非常实用。
如何定义Model类:
1. Ext.regModel("Student",{
2.
3. fields:[
4.
5. {name:"name",type:"string"},
6. {name:"class",type:"string"}
7. ]
8. });
Ext.regModel("Student",{
fields:[
{name:"name",type:"string"},
{name:"class",type:"string"}
]
});
Model类中最重要的属性就是Fields,这个属性接受一个数组,用来设置显示数据中所包含的字段,"Student"设置了该类的名字。
Model类的功能:
1. Ext.regModel("Student",{
2. fields:[
3. {name:"name",type:"string"},
4. {name:"class",type:"string",convert:function(val){
5. if(val=="1"){return
6. "一班"};
7. if(val=="2"){return
8. "二班"};
9. if(val=="3"){return
10. "三班"};
11. }}
12. ]
13. });
Ext.regModel("Student",{
fields:[
{name:"name",type:"string"},
{name:"class",type:"string",convert:function(val){
if(val=="1"){return
"一班"};
if(val=="2"){return
"二班"};
if(val=="3"){return
"三班"};
}}
]
});
首先我们来看一下fields属性中的功能,例如:我们现在定义了一个如上的Model类,然后,数据源返回班级(class)这个属性时。格式为(1、2、3),如果我们把这样的数据呈现出来,那么将显示的很不友好。所以,我们要在呈现之前,对数据做一个转换。把原本不友好的数据,转换成有好的数据,这就用到了。fields中的convert属性。
1. var student=new Student({
2. name:"test",
3.
4.
5. class:"3"
6.
7.
8. });
var student=new Student({
name:"test",
class:"3"
});
2、定义好一个Model类之后,我们就可以使用这个Model类。最简单的方式,我们是直接new一个Model类的对象,然后将我们的数据信息加载到对象中,有点类似于Ext3.X中的record的对象的使用。
上述代码我们根据Student的模型类,定义了一个该类的对象,但在实际应用程序中,像这样的情况很少,毕竟我们的数据不一定都是这种一成不变的数据,有时候我们需要从后台加载我们的数据,然后给于我们的model类。这就需要我们的模型类有能够请求后台的能力,这也是Model中提供的第二个功能,Model中提供一个属性proxy(代理)。这个proxy中有几个比较重要的属性(type、url、reader),type属性值是一个字符串形式,用来表明。该代理的一种类型,具体的可以查看API中了解有哪些类型,url也就是请求后台的url。reader,也就是我们要用什么样的阅读器,来解析我们的数据:
1. Ext.regModel("Student",{
2.
3.
4. fields:[
5.
6.
7. {name:"name",type:"string"},
8. {name:"class",type:"string",convert:function(val){
9. if(val=="1"){return "一班"};
10. if(val=="2"){return "二班"};
11. if(val=="3"){return "三班"};
12. }}
13. ],
14. proxy:{
15. type:"rest",
16. url:"server/service.aspx",
17. reader:"json"
18. }
19. });
20.
21.
22. Student.load(001,{
23.
24.
25. success:function(student){
26. //处理加载完成的逻辑
27. }
28. });
Ext.regModel("Student",{
fields:[
{name:"name",type:"string"},
{name:"class",type:"string",convert:function(val){
if(val=="1"){return "一班"};
if(val=="2"){return "二班"};
if(val=="3"){return "三班"};
}}
],
proxy:{
type:"rest",
url:"server/service.aspx",
reader:"json"
}
});
Student.load(001,{
success:function(student){
//处理加载完成的逻辑
}
});
service.aspx返回的数据格式:
{id:001,name:"zhangsan",class:"2"}
经过上述的设置,我们的Model就可以与后台交互,并要求后台返回我们想要的数据了,这个也是之前record类所办不到的。
3、在我们的应用程序中,可能我们定义的Model不止一个。但是,他们之间可能都是独立的,没有任何的关系,但是,在我们的应用程序中可能在后台返回的数据中存在一定的联系,而且我们又想让这些Model之间存在一定的联系,这样我们在处理起来,会比较简单一些。大家看下边的一段数据。
1. {
2. id:"009",
3. name:"Jerry",
4. subjects:[
5. {id:"001",name:"English"},
6.
7.
8. {id:"002",name:"Mathematics"}
9. ]
10. }
{
id:"009",
name:"Jerry",
subjects:[
{id:"001",name:"English"},
{id:"002",name:"Mathematics"}
]
}
在上述的数据中,科目和学生之间是有一定的联系的。所以,我们也在想,解析数据的时候,让他们保持这种联系,以便于我们更好的解析数据。这样我们就用到了Model中的belongsTo和hasMany这样两个属性。首先要解析这样的数据,我们需要定义好我们的Model类。如下:
1. Ext.regModel("subject",{
2.
3.
4. fields:[
5. {name:"id",type:"string"},
6. {name:"name",type:"string"}
7. ],
8. belongsTo:"Stdudent"
9. });
10.
11.
12.
13.
14. Ext.regModel("Student",{
15. fields:[
16. {name:"id",type:"string"},
17. {name:"name",type:"string"}
18.
19.
20. ],
21.
22.
23. proxy:{
24. type:"rest",
25. url:"servicee/servicee.aspx",
26. reader:"json"
27. },
28. hasMany:[{model:"subject",name:"subjects"}]
29.
30.
31. });
Ext.regModel("subject",{
fields:[
{name:"id",type:"string"},
{name:"name",type:"string"}
],
belongsTo:"Stdudent"
});
Ext.regModel("Student",{
fields:[
{name:"id",type:"string"},
{name:"name",type:"string"}
],
proxy:{
type:"rest",
url:"servicee/servicee.aspx",
reader:"json"
},
hasMany:[{model:"subject",name:"subjects"}]
});
在我们定义好Model后,下面我们就可以加载并解析我们的数据了。
1. Student.load("009",{
2.
3.
4. success:function(student){
5.
6.
7. alert(student.get("id"));
8.
9.
10. alert(student.subjects().getCount());//我们可以直接访问该学生的科目
11. }
12. })
13. });
Student.load("009",{
success:function(student){
alert(student.get("id"));
alert(student.subjects().getCount());//我们可以直接访问该学生的科目
}
})
});
4、在ExtJS4的Model中还提供了对字段列的验证功能。我们想验证字段只需要设置Model类的validations属性即可,代码如下:
1. Ext.regModel("Student",{
2. fields:[
3. {name:"id",type:"string"},
4. {name:"name",type:"string"}
5.
6.
7. ],
8.
9.
10. proxy:{
11. type:"rest",
12. url:"data/1.aspx",
13. reader:"json"
14. },
15. hasMany:[{model:"subject",name:"subjects"}],
16.
17.
18. validations:[
19. {type:"presence",field:"id"},
20.
21.
22. {type:"length",field:"name",min:3}
23. ]
24.
25.
26. });
27.
28.
29. var student=new Student({id:"001",name:"z"});
30.
31.
32. var stuvalidate=student.validate();//得到验证类
33.
34.
35. stuvalidate.isValid();//返回验证结果true或false
36.
37.
38. stuvalidate.each(function(error){
39.
40.
41. alert(error.field+" "+error.message);//遍历验证的信息
42. });
Ext.regModel("Student",{
fields:[
{name:"id",type:"string"},
{name:"name",type:"string"}
],
proxy:{
type:"rest",
url:"data/1.aspx",
reader:"json"
},
hasMany:[{model:"subject",name:"subjects"}],
validations:[
{type:"presence",field:"id"},
{type:"length",field:"name",min:3}
]
});
var student=new Student({id:"001",name:"z"});
var stuvalidate=student.validate();//得到验证类
stuvalidate.isValid();//返回验证结果true或false
stuvalidate.each(function(error){
alert(error.field+" "+error.message);//遍历验证的信息
});
4.16 Combobox三级联动
很多网友在问,Extjs4.0 ComboBox如何实现,好在之前用3.x实现过一个三级联动,如今用Extjs4.0来实现同样的联动效果。其中注意的一点就是,3.x中的model:'local'在Extjs4.0中用queryMode: 'local'来表示,而且在3.x中Load数据时用reload,但是在extjs4.0中要使用load来获取数据。如下图:
代码部分
先看HTML代码:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <title>MHZG.NET-城市三级联动实例</title>
6. <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
7. <script type="text/javascript" src="../../bootstrap.js"></script>
8. <script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
9. <script type="text/javascript" src="combobox.js"></script>
10. </head>
11.
12. <body>
13. </body>
14. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>MHZG.NET-城市三级联动实例</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../bootstrap.js"></script>
<script type="text/javascript" src="../../locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="combobox.js"></script>
</head>
<body>
</body>
</html>
简单的很,就是加载了基本的CSS文件和JS文件,并且加载自定义的combobox.js文件。
combobox.js:
1. Ext.require('Ext.*');
2. Ext.onReady(function(){
3. //定义ComboBox模型
4. Ext.define('State', {
5. extend: 'Ext.data.Model',
6. fields: [
7. {type: 'int', name: 'id'},
8. {type: 'string', name: 'cname'}
9. ]
10. });
11.
12. //加载省数据源
13. var store = Ext.create('Ext.data.Store', {
14. model: 'State',
15. proxy: {
16. type: 'ajax',
17. url: 'city.asp?act=sheng&n='+new Date().getTime()+''
18. },
19. autoLoad: true,
20. remoteSort:true
21. });
22. //加载市数据源
23. var store1 = Ext.create('Ext.data.Store', {
24. model: 'State',
25. proxy: {
26. type: 'ajax',
27. url: 'city.asp?act=shi&n='+new Date().getTime()+''
28. },
29. autoLoad: false,
30. remoteSort:true
31. });
32. //加载区数据源
33. var store2 = Ext.create('Ext.data.Store', {
34. model: 'State',
35. proxy: {
36. type: 'ajax',
37. url: 'city.asp?act=qu&n='+new Date().getTime()+''
38. },
39. autoLoad: false,
40. remoteSort:true
41. });
42.
43.
44.
45. Ext.create("Ext.panel.Panel",{
46. renderTo: document.body,
47. width:290,
48. height:220,
49. title:"城市三级联动",
50. plain: true,
51. margin:'30 10 0 80',
52. bodyStyle: "padding: 45px 15px 15px 15px;",
53. defaults :{
54. autoScroll: true,
55. bodyPadding: 10
56. },
57. items:[{
58. xtype:"combo",
59. name:'sheng',
60. id : 'sheng',
61. fieldLabel:'选择省',
62. displayField:'cname',
63. valueField:'id',
64. store:store,
65. triggerAction:'all',
66. queryMode: 'local',
67. selectOnFocus:true,
68. forceSelection: true,
69. allowBlank:false,
70. editable: true,
71. emptyText:'请选择省',
72. blankText : '请选择省',
73. listeners:{
74. select:function(combo, record,index){
75. try{
76. //userAdd = record.data.name;
77. var parent=Ext.getCmp('shi');
78. var parent1 = Ext.getCmp("qu");
79. parent.clearValue();
80. parent1.clearValue();
81. parent.store.load({params:{param:this.value}});
82. }
83. catch(ex){
84. Ext.MessageBox.alert("错误","数据加载失败。");
85. }
86. }
87. }
88. },
89. {
90. xtype:"combo",
91. name:'shi',
92. id : 'shi',
93. fieldLabel:'选择市',
94. displayField:'cname',
95. valueField:'id',
96. store:store1,
97. triggerAction:'all',
98. queryMode: 'local',
99. selectOnFocus:true,
100. forceSelection: true,
101. allowBlank:false,
102. editable: true,
103. emptyText:'请选择市',
104. blankText : '请选择市',
105. listeners:{
106. select:function(combo, record,index){
107. try{
108. //userAdd = record.data.name;
109. var parent = Ext.getCmp("qu");
110. parent.clearValue();
111. parent.store.load({params:{param:this.value}});
112. }
113. catch(ex){
114. Ext.MessageBox.alert("错误","数据加载失败。");
115. }
116. }
117. }
118. },
119. {
120. xtype:"combo",
121. name:'qu',
122. id : 'qu',
123. fieldLabel:'选择区',
124. displayField:'cname',
125. valueField:'id',
126. store:store2,
127. triggerAction:'all',
128. queryMode: 'local',
129. selectOnFocus:true,
130. forceSelection: true,
131. allowBlank:false,
132. editable: true,
133. emptyText:'请选择区',
134. blankText : '请选择区',
135. }
136. ]
137. })
138. });
Ext.require('Ext.*');
Ext.onReady(function(){
//定义ComboBox模型
Ext.define('State', {
extend: 'Ext.data.Model',
fields: [
{type: 'int', name: 'id'},
{type: 'string', name: 'cname'}
]
});
//加载省数据源
var store = Ext.create('Ext.data.Store', {
model: 'State',
proxy: {
type: 'ajax',
url: 'city.asp?act=sheng&n='+new Date().getTime()+''
},
autoLoad: true,
remoteSort:true
});
//加载市数据源
var store1 = Ext.create('Ext.data.Store', {
model: 'State',
proxy: {
type: 'ajax',
url: 'city.asp?act=shi&n='+new Date().getTime()+''
},
autoLoad: false,
remoteSort:true
});
//加载区数据源
var store2 = Ext.create('Ext.data.Store', {
model: 'State',
proxy: {
type: 'ajax',
url: 'city.asp?act=qu&n='+new Date().getTime()+''
},
autoLoad: false,
remoteSort:true
});
Ext.create("Ext.panel.Panel",{
renderTo: document.body,
width:290,
height:220,
title:"城市三级联动",
plain: true,
margin:'30 10 0 80',
bodyStyle: "padding: 45px 15px 15px 15px;",
defaults :{
autoScroll: true,
bodyPadding: 10
},
items:[{
xtype:"combo",
name:'sheng',
id : 'sheng',
fieldLabel:'选择省',
displayField:'cname',
valueField:'id',
store:store,
triggerAction:'all',
queryMode: 'local',
selectOnFocus:true,
forceSelection: true,
allowBlank:false,
editable: true,
emptyText:'请选择省',
blankText : '请选择省',
listeners:{
select:function(combo, record,index){
try{
//userAdd = record.data.name;
var parent=Ext.getCmp('shi');
var parent1 = Ext.getCmp("qu");
parent.clearValue();
parent1.clearValue();
parent.store.load({params:{param:this.value}});
}
catch(ex){
Ext.MessageBox.alert("错误","数据加载失败。");
}
}
}
},
{
xtype:"combo",
name:'shi',
id : 'shi',
fieldLabel:'选择市',
displayField:'cname',
valueField:'id',
store:store1,
triggerAction:'all',
queryMode: 'local',
selectOnFocus:true,
forceSelection: true,
allowBlank:false,
editable: true,
emptyText:'请选择市',
blankText : '请选择市',
listeners:{
select:function(combo, record,index){
try{
//userAdd = record.data.name;
var parent = Ext.getCmp("qu");
parent.clearValue();
parent.store.load({params:{param:this.value}});
}
catch(ex){
Ext.MessageBox.alert("错误","数据加载失败。");
}
}
}
},
{
xtype:"combo",
name:'qu',
id : 'qu',
fieldLabel:'选择区',
displayField:'cname',
valueField:'id',
store:store2,
triggerAction:'all',
queryMode: 'local',
selectOnFocus:true,
forceSelection: true,
allowBlank:false,
editable: true,
emptyText:'请选择区',
blankText : '请选择区',
}
]
})
});
上述代码中,如果在ComboBox直接定义store数据源,会出现这样一种情况,那就是当选择完第一个省,点击第二个市的时候,会闪一下,再点击,才会出现市的数据。那么要解决这样的情况,那么必须先要定义好省、市、区的数据源。那么再点击的时候,就不会出现上述情况了。
代码中,使用store为省的数据,设置其autoLoad为true,那么页面第一次加载的时候,就会自动加载省的数据,然后给省和市添加了监听select,作用在于当点击省的时候,要清空市和区的数据,并根据当前选定的值去加载对应的数据到市的数据源中。当然store1和store2原理是一样的。
最后,服务端要根据传的值进行数据检索及返回正确数据,这里没有从数据库中查询数据,而只是简单的写了一些测试代码,相信extjs代码没有任何的问题了,那么服务端返回数据,就不是一件很重要的事情了。
City.asp:
1. <%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
2. <%
3. Response.ContentType = "text/html"
4. Response.Charset = "UTF-8"
5. %>
6. <%
7. Dim act:act = Request("act")
8. Dim param : param = Request("param")
9. If act = "sheng" Then
10. Response.Write("[")
11. Response.Write("{""cname"":""北京市"",""id"":""110000""},")
12. Response.Write("{""cname"":""内蒙古自治区"",""id"":""150000""}")
13. Response.Write("]")
14. End If
15. If act = "shi" Then
16. If param = "110000" Then
17. Response.Write("[")
18. Response.Write("{""cname"":""市辖区"",""id"":""110100""},")
19. Response.Write("{""cname"":""市辖县"",""id"":""110200""}")
20. Response.Write("]")
21. ElseIf param = "150000" Then
22. Response.Write("[")
23. Response.Write("{""cname"":""呼和浩特市"",""id"":""150100""},")
24. Response.Write("{""cname"":""包头市"",""id"":""150200""}")
25. Response.Write("]")
26. End If
27. End If
28. If act = "qu" Then
29. If param = "110100" Then
30. Response.Write("[")
31. Response.Write("{""cname"":""朝阳区"",""id"":""110101""},")
32. Response.Write("{""cname"":""昌平区"",""id"":""110102""}")
33. Response.Write("]")
34. ElseIf param = "110200" Then
35. Response.Write("[")
36. Response.Write("{""cname"":""密云县"",""id"":""110201""},")
37. Response.Write("{""cname"":""房山县"",""id"":""110202""}")
38. Response.Write("]")
39. ElseIf param = "150100" Then
40. Response.Write("[")
41. Response.Write("{""cname"":""回民区"",""id"":""150101""},")
42. Response.Write("{""cname"":""新城区"",""id"":""150102""}")
43. Response.Write("]")
44. ElseIf param = "150200" Then
45. Response.Write("[")
46. Response.Write("{""cname"":""青山区"",""id"":""150201""},")
47. Response.Write("{""cname"":""东河区"",""id"":""150202""}")
48. Response.Write("]")
49. End If
50. End If
51. %>
<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<%
Response.ContentType = "text/html"
Response.Charset = "UTF-8"
%>
<%
Dim act:act = Request("act")
Dim param : param = Request("param")
If act = "sheng" Then
Response.Write("[")
Response.Write("{""cname"":""北京市"",""id"":""110000""},")
Response.Write("{""cname"":""内蒙古自治区"",""id"":""150000""}")
Response.Write("]")
End If
If act = "shi" Then
If param = "110000" Then
Response.Write("[")
Response.Write("{""cname"":""市辖区"",""id"":""110100""},")
Response.Write("{""cname"":""市辖县"",""id"":""110200""}")
Response.Write("]")
ElseIf param = "150000" Then
Response.Write("[")
Response.Write("{""cname"":""呼和浩特市"",""id"":""150100""},")
Response.Write("{""cname"":""包头市"",""id"":""150200""}")
Response.Write("]")
End If
End If
If act = "qu" Then
If param = "110100" Then
Response.Write("[")
Response.Write("{""cname"":""朝阳区"",""id"":""110101""},")
Response.Write("{""cname"":""昌平区"",""id"":""110102""}")
Response.Write("]")
ElseIf param = "110200" Then
Response.Write("[")
Response.Write("{""cname"":""密云县"",""id"":""110201""},")
Response.Write("{""cname"":""房山县"",""id"":""110202""}")
Response.Write("]")
ElseIf param = "150100" Then
Response.Write("[")
Response.Write("{""cname"":""回民区"",""id"":""150101""},")
Response.Write("{""cname"":""新城区"",""id"":""150102""}")
Response.Write("]")
ElseIf param = "150200" Then
Response.Write("[")
Response.Write("{""cname"":""青山区"",""id"":""150201""},")
Response.Write("{""cname"":""东河区"",""id"":""150202""}")
Response.Write("]")
End If
End If
%>
5 员工管理系统
5.1 准备工作
重写原因:由于开始准备的时候,就是按照传统开发去做的,写了一部分之后,有网友和同事提出:“为什么不用MVC模式”呢?这样的问题让我对目前传统开发的心发生了一些细微变法,对啊,为什么不用MVC模式呢?我征求了一下同事及热心网友的意见,都同意使用MVC模式开发。从而,我删掉了原来所有目录,重新按照MVC模式去组织目录结构,很快的,目录结构准备好了。
那。。我们重新来过,使用EXTJS4.0的MVC模式,开发这套员工管理系统。给个简称吧。SMS(你懂得。呵呵!)。
一、建立环境:
Data:数据库文件夹,里面放着管理系统用的数据库文件。数据库目前只有三张表。分别是:
Menu:菜单项
user:员工注册信息
userinfo:员工个人资料信息
Images:图片目录,一些自定义的图片文件
Include:服务端文件目录,里面包含ASP所用到的Conn.asp、Function.asp等文件
App:整个SMS所用到的自定义JS文件,里面有一个controller文件夹,一个view文件夹。controller文件夹放置主代码,view文件夹放置各组件。这几个文件夹中的内容会在第二章进行介绍。
Extjs4:此目录放置Extjs4的库文件。
Server:服务端目录,里面包含ASP服务端获取数据的各种.ASP文件。目前里面建立了一个叫MenuLoader.asp的文件,从名字上来看,这个文件是加载菜单使用。
5.2 框架的搭建
废话不多说了,上篇文章建立了比较基础的文件。今天开始搭建大体的框架,由于Extjs4在组件建立方面有了很大的改变,所以第一次建立的框架页面还是费了比较长的时间。本章内容增加了一些图片及CSS文件,目的是为了美化整个界面。增加的CSS文件:
注意事项:layout、region的使用,如果没有看API及相关文档的话,那么面对错误对话框的时候,还不知道是怎么回事。
本文将main.js放到了/app/controller文件夹下,这将是整个项目的主体文件。
而头部、菜单、内容区及底部则完全分离成4个JS文件,我们将先实现这几个文件的基础功能,之后我们会慢慢完善这些组件。而整个页面的填充,也使用一个JS文件来完成。也许有人会问,这么多文件,是不是要都在index.html中引入啊。这样想的话,就错了哦。因为我们使用的是Extjs4,所以我们一定要使用Extjs4 动态加载功能。
先来看下自定义CSS(sytle.css):
1. #header { background: #7F99BE url(/images/layout-browser-hd-bg.gif) repeat-x center;}
2. #header h1 {font-size: 16px;color: #fff;padding: 3px 10px; font-family:"微软雅黑","黑体"}
3.
4. .tabs{}
5. .tabs{background-image: url(../images/menuPanel/bulletin_manager.gif) !important;}
6. .manage{background-image: url(../images/menuPanel/admin.gif) !important;}
7. .home{background-image: url(../images/home.gif) !important; line-height:30px;}
8. .icon-menu{background-image: url(../images/menuPanel/sys.gif) !important;}
#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代码为:
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml">
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5. <title>员工管理系统</title>
6. <link rel="stylesheet" type="text/css" href="extjs4/resources/css/ext-all.css" />
7. <!--引入自定义CSS-->
8. <link rel="stylesheet" type="text/css" href="extjs4/resources/style.css" />
9. <script type="text/javascript" src="extjs4/ext-all-debug.js"></script>
10. <script type="text/javascript" src="app.js"></script>
11. </head>
12.
13. <body>
14. </body>
15. </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>员工管理系统</title>
<link rel="stylesheet" type="text/css" href="extjs4/resources/css/ext-all.css" />
<!--引入自定义CSS-->
<link rel="stylesheet" type="text/css" href="extjs4/resources/style.css" />
<script type="text/javascript" src="extjs4/ext-all-debug.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
</body>
</html>
App.js:
1. Ext.Loader.setConfig({enabled: true});
2. Ext.application({
3. name: 'SMS',
4. appFolder: 'app',
5. controllers: [
6. 'Main'
7. ]
8. });
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:
1. Ext.define('SMS.controller.Main',{
2. extend: 'Ext.app.Controller',
3. init : function(){
4. }
5. })
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:这个是头部,也就是深蓝色底子。白色字体,那块,上面写着员工管理系统。
代码为:
1. Ext.define('SMS.view.Header', {
2. extend: 'Ext.Component',
3. initComponent: function() {
4. Ext.applyIf(this, {
5. xtype: 'box',
6. cls: 'header',
7. region: 'north',
8. html: '<h1>员工管理系统</h1>',
9. height: 30
10. });
11. this.callParent(arguments);
12. }
13. });
Ext.define('SMS.view.Header', {
extend: 'Ext.Component',
initComponent: function() {
Ext.applyIf(this, {
xtype: 'box',
cls: 'header',
region: 'north',
html: '<h1>员工管理系统</h1>',
height: 30
});
this.callParent(arguments);
}
});
Menu.js:
1. Ext.define('SMS.view.Menu',{
2. extend: 'Ext.tree.Panel',
3. initComponent : function(){
4. Ext.apply(this,{
5. id: 'menu-panel',
6. title: '系统菜单',
7. iconCls:'icon-menu',
8. margins : '0 0 -1 1',
9. region:'west',
10. border : false,
11. enableDD : false,
12. split: true,
13. width : 212,
14. minSize : 130,
15. maxSize : 300,
16. rootVisible: false,
17. containerScroll : true,
18. collapsible : true,
19. autoScroll: false
20. });
21. this.callParent(arguments);
22. }
23. })
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:
1. Ext.define('SMS.view.TabPanel',{
2. extend: 'Ext.tab.Panel',
3. initComponent : function(){
4. Ext.apply(this,{
5. id: 'content-panel',
6. region: 'center',
7. defaults: {
8. autoScroll:true,
9. bodyPadding: 10
10. },
11. activeTab: 0,
12. border: false,
13. //plain: true,
14. items: [{
15. id: 'HomePage',
16. title: '首页',
17. iconCls:'home',
18. layout: 'fit'
19. }]
20. });
21. this.callParent(arguments);
22. }
23. })
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:
1. Ext.define('SMS.view.South',{
2. extend: 'Ext.Toolbar',
3. initComponent : function(){
4. Ext.apply(this,{
5. id:"bottom",
6. //frame:true,
7. region:"south",
8. height:23,
9. items:["当前用户:Guest",'->',"技术支持:<a href='http://www.mhzg.net' target='_blank' style='text-decoration:none;'><font color='#0000FF'>http://www.mhzg.net</font></a> "]
10. });
11. this.callParent(arguments);
12. }
13. })
Ext.define('SMS.view.South',{
extend: 'Ext.Toolbar',
initComponent : function(){
Ext.apply(this,{
id:"bottom",
//frame:true,
region:"south",
height:23,
items:["当前用户:Guest",'->',"技术支持:<a href='http://www.mhzg.net' target='_blank' style='text-decoration:none;'><font color='#0000FF'>http://www.mhzg.net</font></a> "]
});
this.callParent(arguments);
}
})
文件都创建好了。我们进行最后一部,布局。
Viewport.js:
1. Ext.define('SMS.view.Viewport',{
2. extend: 'Ext.Viewport',
3. layout: 'fit',
4. hideBorders: true,
5. requires : [
6. 'SMS.view.Header',
7. 'SMS.view.Menu',
8. 'SMS.view.TabPanel',
9. 'SMS.view.South'
10. ],
11. initComponent : function(){
12. var me = this;
13. Ext.apply(me, {
14. items: [{
15. id:'desk',
16. layout: 'border',
17. items: [
18. Ext.create('SMS.view.Header'),
19. Ext.create('SMS.view.Menu'),
20. Ext.create('SMS.view.TabPanel'),
21. Ext.create('SMS.view.South')
22. ]
23. }]
24. });
25. me.callParent(arguments);
26. }
27. })
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的风格,也完全符合我的预期。
最近有网友跟我反映,说按照文章内容照着完成后,不显示框架,只有空白一片,问了几位网友下,发现他们都是使用的Exjts4.02a的版本,最后,在官方最新的API中发现,Ext.application 下的属性autoCreateViewport 默认变成了false,如此一来,就不能自动创建Viewport了。这里提供下解决办法。
如果你的Extjs4版本为Extjs4.02a以下版本,则不需要任何改动。完全可以显示完整框架。
如果你的Extjs4版本为Extjs4.02a或以上版本,则需要修改app.js文件,其内容为:
1. Ext.Loader.setConfig({enabled: true});
2. Ext.application({
3. name: 'SMS',
4. autoCreateViewport: true,
5. appFolder: 'app',
6. controllers: [
7. 'Main'
8. ]
9. });
Ext.Loader.setConfig({enabled: true});
Ext.application({
name: 'SMS',
autoCreateViewport: true,
appFolder: 'app',
controllers: [
'Main'
]
});
注意,此app.js加入了autoCreateViewport: true设置。
5.3 菜单的实现
上篇文章介绍了搭建一个空的框架,使得管理系统有了大致的模样,今天工作的主要内容就是菜单的实现以及点击菜单后在右边内容区打开一个新的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数据格式如下:
1. [{"text":"管理员管理","id":"1","iconCls":"manage","leaf":true}]
[{"text":"管理员管理","id":"1","iconCls":"manage","leaf":true}]
这里注意一点:由于整棵菜单树都是异步获取的,所以节点并不需要递归,而树的node.id就是JSON数据中的ID。点击父节点展开子节点的时候,发送的数据也是node.id,这样正好解决获取子节点的问题。所有根节点的ParentID都为0。那么第一次加载菜单的时候,正好可以获取所有的根节点了。
重点,菜单树的数据源编写,根据MVC原则,在app目录下建立store文件夹,这个文件夹下放置所有的获取数据的js文件,在store文件夹下建立Menus.js,编写如下代码:
1. Ext.define('SMS.store.Menus',{
2. extend: 'Ext.data.TreeStore',
3. root: {
4. expanded: true
5. },
6. proxy: {
7. type: 'ajax',
8. url: '/server/MenuLoader.asp'
9. }
10.
11. })
Ext.define('SMS.store.Menus',{
extend: 'Ext.data.TreeStore',
root: {
expanded: true
},
proxy: {
type: 'ajax',
url: '/server/MenuLoader.asp'
}
})
然后修改view文件夹下的Menu.js文件:
1. Ext.define('SMS.view.Menu',{
2. extend: 'Ext.tree.Panel',
3. alias: 'widget.smsmenu',
4. requires:['SMS.store.Menus'],
5. initComponent : function(){
6. Ext.apply(this,{
7. id: 'menu-panel',
8. title: '系统菜单',
9. iconCls:'icon-menu',
10. margins : '0 0 -1 1',
11. region:'west',
12. border : false,
13. enableDD : false,
14. split: true,
15. width : 212,
16. minSize : 130,
17. maxSize : 300,
18. rootVisible: false,
19. containerScroll : true,
20. collapsible : true,
21. autoScroll: false,
22. store:Ext.create('SMS.store.Menus'),
23. });
24. this.callParent(arguments);
25. }
26. })
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会完成这一系列的工作,具体代码如下:
1. Ext.define('SMS.controller.Menu',{
2. extend: 'Ext.app.Controller',
3. refs:[
4. {ref: 'smsmenu',selector: 'smstablepanel'},
5. {ref: 'tabPanel',selector:'smstablepanel'}
6. ],
7. init:function(){
8. this.control({
9. 'smsmenu': {
10. itemmousedown: this.loadMenu
11. }
12. })
13. },
14. loadMenu:function(selModel, record){
15. if (record.get('leaf')) {
16. var panel = Ext.getCmp(record.get('id'));
17. if(!panel){
18. panel ={
19. title: 'New Tab ' + record.get('id'),
20. iconCls: 'tabs',
21. html: 'Tab Body ' + record.get('id') + '<br/><br/>',
22. closable: true
23. }
24. this.openTab(panel,record.get('id'));
25. }else{
26. var main = Ext.getCmp("content-panel");
27. main.setActiveTab(panel);
28. }
29. }
30.
31. },
32. openTab : function (panel,id){
33. var o = (typeof panel == "string" ? panel : id || panel.id);
34. var main = Ext.getCmp("content-panel");
35. var tab = main.getComponent(o);
36. if (tab) {
37. main.setActiveTab(tab);
38. } else if(typeof panel!="string"){
39. panel.id = o;
40. var p = main.add(panel);
41. main.setActiveTab(p);
42. }
43. }
44.
45. })
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') + '<br/><br/>',
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:
1. Ext.Loader.setConfig({enabled: true});
2. Ext.application({
3. name: 'SMS',
4. appFolder: 'app',
5. autoCreateViewport:true,
6. controllers: [
7. 'Menu'
8. ]
9. });
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,使其可以使菜单项正常使用。
5.4 实现登录
上篇文章介绍了如何实现菜单功能(点击查看),但是有个问题,就是管理系统必须是登录后才会显示菜单,而且菜单还要实现不同权限有不同的菜单项,本文将实现这个功能。
首先,将server/MenuLoader.asp修改,增加管理员验证功能。即
1. If Session("Manage") <> "" Then
2. '显示菜单项
3. End If
If Session("Manage") <> "" Then
'显示菜单项
End If
这时,重新打开页面,由于有了基本的管理员验证,菜单不显示了。
接下来,开始制作登录,在view文件夹下建立Login.js,checkcode.js,其中Login.js实现登录功能,有用户名、密码和验证码,验证码的实现,就是checkcode.js,由于篇幅问题,checkcode.js请查看本站另一篇文章, ExtJS4学习笔记(十)---ExtJS4图片验证码的实现。
主要是Login.js:
1. Ext.define(SMS.view.Login',{
2. extend:'Ext.window.Window',
3. alias: 'widget.loginForm',
4. requires: ['Ext.form.*','SMS.view.CheckCode'],
5. initComponent:function(){
6. var checkcode = Ext.create('SMS.view.CheckCode',{
7. cls : 'key',
8. fieldLabel : '验证码',
9. name : 'CheckCode',
10. id : 'CheckCode',
11. allowBlank : false,
12. isLoader:true,
13. blankText : '验证码不能为空',
14. codeUrl: '/include/checkCode.asp',
15. width : 160
16. })
17. var form = Ext.widget('form',{
18. border: false,
19. bodyPadding: 10,
20. fieldDefaults: {
21. labelAlign: 'left',
22. labelWidth: 55,
23. labelStyle: 'font-weight:bold'
24. },
25. defaults: {
26. margins: '0 0 10 0'
27. },
28. items:[{
29. xtype: 'textfield',
30. fieldLabel: '用户名',
31. blankText : '用户名不能为空',
32. name:'UserName',
33. id:'UserName',
34. allowBlank: false,
35. width:240
36. },{
37. xtype: 'textfield',
38. fieldLabel: '密 码',
39. allowBlank: false,
40. blankText : '密码不能为空',
41. name:'PassWord',
42. id:'PassWord',
43. width:240,
44. inputType : 'password'
45. },checkcode],
46. buttons:[{
47. text:'登录',
48. handler:function(){
49. var form = this.up('form').getForm();
50. var win = this.up('window');
51. if(form.isValid()){
52. form.submit({
53. clientValidation: true,
54. waitMsg:'请稍后',
55. waitTitle:'正在验证登录',
56. url:'/server/checklogin.asp',
57. success: function(form, action) {
58. //登录成功后。
59. //隐藏登录窗口,并重新加载菜单
??
??
??
??
60. win.hide();
61. Ext.getCmp('SystemMenus').store.load();
62.
63. },
64. failure: function(form, action) {
65. Ext.MessageBox.show({
66. width:150,
67. title:"登录失败",
68. buttons: Ext.MessageBox.OK,
69. msg:action.result.msg
70. })
71. }
72. });
73. }
74. }
75. }]
76. })
77. Ext.apply(this,{
78. height: 160,
79. width: 280,
80. title: '用户登陆',
81. closeAction: 'hide',
82. closable : false,
83. iconCls: 'win',
84. layout: 'fit',
85. modal : true,
86. plain : true,
87. resizable: false,
88. items:form
89. });
90. this.callParent(arguments);
91. }
92. });
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:
1. Ext.define(SMS.controller.Main',{
2. extend: 'Ext.app.Controller',
3. requires:['SMS.view.Login'],
4.
5. onLaunch : function(){
6. var win;
7. if(!win){
8. win = Ext.create('SMS.view.Login').show();
9. }
10. }
11. })
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();
}
}
})
这时,当页面加载的时候,会显示登录窗口,而登录成功后, 会隐藏登录窗口并加载菜单。最后附上登录成功后页面效果。
5.5 动态grid
上一节我们说了如何实现登录,现在为止,框架有了,菜单有了。下一步工作就是实现每个菜单项目的数据显示,一般来讲,我们都是固定思维,通过编写大量的grid来实现各种数据的显示,在网络上有一些案例,都是每一个菜单项目,写一个grid,而且点击树节点,都是通过判断或其他方法来创建相应grid的实例,例如:
1. if(node.id=="depmanage"){
2. panel = new depManagePanel();
3. main.openTab(panel);
4. }else if(node.id=="telmanage"){
5. panel = new telManagePanel();
6. main.openTab(panel);
7. }else if(node.id=="usermanage"){
8. panel = new userManagePanel();
9. main.openTab(panel);
10. }else if(node.id=="userlog"){
11. panel = new operationPanel();
12. main.openTab(panel);
13. }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文件夹下。其代码为:
1. Ext.define('SMS.model.Menu', {
2. extend: 'Ext.data.Model',
3. fields: ['id', 'text','iconCls','stores','columns'],
4. root: {
5. expanded: true
6. },
7. proxy: {
8. type: 'ajax',
9. url: '/server/MenuLoader.asp'
10. }
11. })
12.
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:
1. [{"text":"备案列表","id":"4","iconCls":"Folder","stores":"bastore","columns":[{text:'序号',dataIndex:'ID'},{text:'公司名称',dataIndex:'kehu_name'}],"leaf":true},{"text":"新增备案","id":"5","iconCls":"Folder","stores":"","columns":[],"leaf":true}]
2.
[{"text":"备案列表","id":"4","iconCls":"Folder","stores":"bastore","columns":[{text:'序号',dataIndex:'ID'},{text:'公司名称',dataIndex:'kehu_name'}],"leaf":true},{"text":"新增备案","id":"5","iconCls":"Folder","stores":"","columns":[],"leaf":true}]
修改app/store下的Menus.js,更改为如下代码:
1. Ext.define('SMS.store.Menus',{
2. extend: 'Ext.data.TreeStore',
3. requires: 'SMS.model.Menu',
4. model: 'SMS.model.Menu'
5. })
Ext.define('SMS.store.Menus',{
extend: 'Ext.data.TreeStore',
requires: 'SMS.model.Menu',
model: 'SMS.model.Menu'
})
新建bastore.js,将其放到app/store目录下,代码为:
1. Ext.define('SMS.store.bastore', {
2. extend: 'Ext.data.Store',
3. requires: 'SMS.model.beianlistmodel',
4. model: 'SMS.model.beianlistmodel'
5. });
6.
7.
8.
Ext.define('SMS.store.bastore', {
extend: 'Ext.data.Store',
requires: 'SMS.model.beianlistmodel',
model: 'SMS.model.beianlistmodel'
});
新建beianlistmodel.js,将其放到app/model目录下,其代码为:
1. Ext.define('SMS.model.beianlistmodel', {
2. extend: 'Ext.data.Model',
3. fields: ['ID', 'kehu_name']//,这里只列出了自增值id和客户名称。
4.
5. //proxy: {
6. // type: 'ajax',
7. // url: '/server/getbeianlist.asp',
8. // reader: {
9. // type: 'json',
10. // root: 'results'
11. // }
12. // }
13. });
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'],
然后将测试代码
1. if (record.get('leaf')) {
2. var panel = Ext.getCmp(record.get('id'));
3. if(!panel){
4. panel ={
5. title: 'New Tab ' + record.get('id'),
6. iconCls: 'tabs',
7. html: 'Tab Body ' + record.get('id') + '<br/><br/>',
8. closable: true
9. }
10. this.openTab(panel,record.get('id'));
11. }else{
12. var main = Ext.getCmp("content-panel");
13. main.setActiveTab(panel);
14. }
15. }
16.
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') + '<br/><br/>',
closable: true
}
this.openTab(panel,record.get('id'));
}else{
var main = Ext.getCmp("content-panel");
main.setActiveTab(panel);
}
}
全部删除,重新写如下代码:
1. if (record.get('leaf')) {
2. var panel = Ext.getCmp(record.get('id'));
3. if(!panel){
4. panel = Ext.create("Ext.grid.Panel",{
5. store:record.get('stores'),
6. columns:record.get('columns'),
7. title: record.get('text'),
8. id:record.get('text')+record.get('id'),
9. viewConfig: {
10. stripeRows: true
11. },
12. closable: true
13. })
14. this.openTab(panel,record.get('id'));
15. }else{
16. var main = Ext.getCmp("content-pane");
17. main.setActiveTab(panel);
18. }
19. }
20.
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":"备案列表","id":"4","iconCls":"Folder","stores":"bastore","columns":[{text:'序号',dataIndex:'ID'},{text:'公司名称',dataIndex:'kehu_name'}],"leaf":true}
我们就实现了动态生成grid。app/controller目录下的Menu.js全部代码为:
1. Ext.define('SMS.controller.Menu',{
2. extend: 'Ext.app.Controller',
3. refs:[
4. {ref: 'smsmenu',selector: 'smstablepanel'},
5. {ref: 'tabPanel',selector:'smstablepanel'}
6.
7. ],
8. stores: ['bastore'],
9. init:function(){
10. this.control({
11. 'smsmenu': {
12. itemmousedown: this.loadMenu
13. }
14. })
15. },
16. loadMenu:function(selModel, record){
17. if (record.get('leaf')) {
18. var panel = Ext.getCmp(record.get('id'));
19. if(!panel){
20. panel = Ext.create("Ext.grid.Panel",{
21. store:record.get('stores'),
22. columns:record.get('columns'),
23. title: record.get('text'),
24. id:record.get('text')+record.get('id'),
25. viewConfig: {
26. stripeRows: true
27. },
28. closable: true
29. })
30. this.openTab(panel,record.get('id'));
31. }else{
32. var main = Ext.getCmp("content-pane");
33. main.setActiveTab(panel);
34. }
35. }
36.
37. },
38. openTab : function (panel,id){
39. var o = (typeof panel == "string" ? panel : id || panel.id);
40. var main = Ext.getCmp("content-pane");
41. var tab = main.getComponent(o);
42. if (tab) {
43. main.setActiveTab(tab);
44. } else if(typeof panel!="string"){
45. panel.id = o;
46. var p = main.add(panel);
47. main.setActiveTab(p);
48. }
49. }
50.
51. })
52.
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 开发笔记,请大家持续关注,谢谢!!
5.6 数据的增删改
上一章,我们介绍了动态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中的数据为:
1. {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文件,增加一些功能,使其可以添加、删除信息。修改内容如下:
1. if (record.get('leaf')) {
2. var panel = Ext.getCmp(record.get('id'));
3. if(!panel){
4. Ext.require(['Ext.grid.*']);
5. var rowEditing = Ext.create('Ext.grid.plugin.RowEditing',{
6. clicksToMoveEditor: 1,
7. autoCancel: true
8. });
9. panel = Ext.create("Ext.grid.Panel",{
10. store:record.get('stores'),
11. columns:record.get('columns'),
12. errorSummary:false,
13. title: record.get('text'),
14. id:record.get('text')+record.get('id'),
15. columnLines: true,
16. bodyPadding:0,
17. closable: true,
18. bbar: Ext.create('Ext.PagingToolbar', {
19. store: record.get('stores'),
20. displayInfo: true,
21. displayMsg: '显示 {0} - {1} 条,共计 {2} 条',
22. emptyMsg: "没有数据"
23. }),
24. dockedItems: [{
25. xtype: 'toolbar',
26. items: [{
27. text: '增加信息',
28. iconCls: 'icon-add',
29. handler: function(){
30. rowEditing.cancelEdit();
31. var panelStore = this.up("panel").getStore();
32. var panelModel = this.up("panel").getSelectionModel();
33. panelStore.insert(0,panelModel);
34. rowEditing.startEdit(0, 0);
35. }
36. }, '-', {
37. itemId: 'delete',
38. text: '删除信息',
39. iconCls: 'icon-delete',
40. disabled: true,
41. handler: function(){
42. var selection = panel.getView().getSelectionModel().getSelection()[0];
43. var panelStore = this.up("panel").getStore();
44. if (selection) {
45. panelStore.remove(selection);
46. }
47. }
48. }, '-' ,{
49. text:'刷新数据',
50. iconCls:'icon-refresh',
51. handler:function(){
52. var panelStore = this.up("panel").getStore();
53. var currPage = panelStore.currentPage;
54. panelStore.removeAll();
55. panelStore.load(currPage);
56. }
57. }]
58. }],
59. plugins: [rowEditing],
60. listeners: {
61. 'selectionchange': function(view, records) {
62. panel.down('#delete').setDisabled(!records.length);
63. }
64. }
65. })
66. this.openTab(panel,record.get('id'));
67. }else{
68. var main = Ext.getCmp("content_panel");
69. main.setActiveTab(panel);
70. }
71. }
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:
1. Ext.define('SMS.store.bastore', {
2. extend: 'Ext.data.Store',
3. requires: 'SMS.model.beianlistmodel',
4. model: 'SMS.model.beianlistmodel',
5. pageSize: 20,
6. remoteSort: true,
7. autoLoad:true,
8. proxy: {
9. type: 'ajax',
10. url: '/server/getbeian.asp',
11. reader: {
12. root: 'items',
13. totalProperty : 'total'
14. },
15. simpleSortMode: true
16. },
17. listeners:{
18. update:function(store,record){
19. var currPage = store.currentPage;
20. //alert(record.get("ID"))
21. Ext.Ajax.request({
22. url:'/server/getbeian.asp?action=save',
23. params:{
24. id : record.get("ID"),
25. kehu_name:record.get("kehu_name"),
26. beianhao:record.get("beianhao"),
27. beianpass:record.get("beianpass"),
28. beianemail:record.get("beianemail"),
29. emailpass:record.get("emailpass"),
30. beianzh:record.get("beianzh"),
31. beianzhpa:record.get("beianzhpa"),
32. },
33. success:function(response){
34. store.removeAll();
35. store.load(currPage);
36. }
37. });
38. },
39. remove:function(store,record){
40. var currPage = store.currentPage;
41. //alert(record.get("ID"))
42. Ext.Ajax.request({
43. url:'/server/getbeian.asp?action=del',
44. params:{
45. id : record.get("ID")
46. },
47. success:function(response){
48. store.removeAll();
49. store.load(currPage);
50. }
51. });
52. }
53. },
54. sorters: [{
55. property: 'ID',
56. direction: 'DESC'
57. }]
58. });
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学习(五)最基本的Ext类
Ext类是ExtJs中最常见.最基础的一个类,它是一个全局对象,封装了所有类.单例和 Sencha 库所提供的实用方法. 大多数用户界面组件在一个较低的层次嵌套在命名空间中, 但是提供的许多常见的实用 ...
- extjs4学习-01-准备工作
想学习这个,在这里做个笔记. 创建了svn管理,路径http://ip:端口/repos/doc_jnfwz/liuzhenming/extjs4/extjs4 eclipse 中安装插件,支持在js ...
- ExtJs4学习(一):正确认识ExtJs4
认识ExtJs 1.Javat能用ExtJs吗? 它是展现层的技术,与JS,HTML,CSS有关.至于server端是.Net,还是PHP等无关. 2.ExtJs适合什么样的项目? 依照官方的说法,E ...
- extjs4学习-02-导入相关文件
在WebContent下创建extjs4目录. 将extjs项目文件中的resource文件夹和ext-all.js.ext-all.js.ext-all-debug.js文件拷贝进去.
- ExtJs4学习(六)MVC中的Model
Model定义的两种方式 第一种 Ext.define("User",{ extend:"Ext.data.Model", fields:[{ name:'us ...
- ExtJs4学习(二):Dom操作
如今主流的JS框架要数ExtJs和JQuery应用的比較广泛.JQuery属于轻量级的,一般做站点应用比較常见.可见块头小的优势. ExtJs比較庞大,它除了对主要的JS语法和HTML DOM操作方式 ...
- ExtJs4学习(三)组件查找 ComponentQuery类
Extjs3.x: ID:这就是所熟知的Ext.getCmp("组件ID"),缺点是id重复导致出错. ref:在EXTJS3中,所有的组件都会有一个ref属性,也就是refere ...
- ExtJs4学习MVC中的Store
Ext.data.Store是extjs中用来进行数据交换和数据交互的标准中间件,无论是Grid还是ComboBox,都是通过它实现数据读取.类型转换.排序分页和搜索等操作的. 1 2 3 4 5 6 ...
- ExtJs4学习(四):Extjs 中id与itemId的差别
为了方便表示或是指定一个组件的名称,我们一般会使用id或者itemId进行标识命名. (推荐尽量使用itemId.这样能够降低页面唯一标识而产生的冲突) id: id是作为整个页面的Comp ...
随机推荐
- IIS 配置问题解决
无法识别的属性“targetFramework”.请注意属性名称区分大小写. 配置错误 说明: 在处理向该请求提供服务所需的配置文件时出错.请检查下面的特定错误详细信息并适当地修改配置文件. 分析器错 ...
- 《编写高质量代码--Web前端开发修炼之道》读书笔记
前言 这两周参加公司的新项目,采用封闭式开发(项目成员在会议室里开发),晚上加班到很晚,所以没时间和精力写原创博客了,今天就分享下这篇<编写高质量代码--Web前端开发修炼之道>读书笔记吧 ...
- Best Time to Buy and Sell sock II
Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...
- [UML]UML中几种类间关系
UML中类间关系主要有六种,分别是继承.实现.依赖.关联.聚合.组合 1.继承 继承是指A类继承B类,继承它private除外的所有属性和方法,这种关系是最常见的关系,在java中使用extends表 ...
- jQuery自带的一些常用方法总结
jQuery自带的一些常用方法总结 这篇文章主要介绍了jQuery自带的一些常用方法总结,包括$.trim .$.contains.$.each.$.map.$.inArray.$.extend等,需 ...
- Android之Activity生命周期简介
概述 有图有真相,所以先上图: 上图是从Android官网截下的Activity的生命周期流程图,结构非常清晰,它描述了Activity在其生命周期中所有可能发生的情况以及发生的先后顺序,下面就将结合 ...
- C语言笔记(枚举)
关于枚举,在我以前写程序的时候,几乎是没有使用过的,只是偶尔会在一下别人写的驱动库中有看到过.今天看了朱老师的视频,准备将枚举相关的东西总结一下. 一.关于枚举,你需要知道 (1)在C语言中就是一些符 ...
- CSU 1160(进制问题)
CSU 1160 Time Limit:1000MS Memory Limit:131072KB 64bit IO Format:%lld & %llu Descrip ...
- 转:玩转Google开源C++单元测试框架Google Test系列
转自http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Google ...
- 转:微博CacheService架构浅析
文章来自于:http://www.infoq.com/cn/articles/weibo-cacheservice-architecture 微博作为国内最大的社交媒体网站之一,每天承载着亿万用户的服 ...