当前位置: 代码迷 >> JavaScript >> 如何让你更快的学会extjs,(包括1.0,2.0,3.0。3个版本)
  详细解决方案

如何让你更快的学会extjs,(包括1.0,2.0,3.0。3个版本)

热度:626   发布时间:2012-10-31 14:37:31.0
怎么让你更快的学会extjs,(包括1.0,2.0,3.0。3个版本)


第1章 闪烁吧!看看extjs那些美丽的例子
1.1. 一切从extjs发布包开始
非常幸运的是,我们可以免费去extjs.com下载ext发布包,里边源代码,api文档,例子一应俱全。不过要是想访问svn获得最新的代码,就要花钱了。不过我们现阶段只要这个免费的发布包就可以了,通过里边的范例,可以让我们体验一下ext的风范。
下载地址:http://www.extjs.com/download。到写文档的此时此刻,咱们可以选择ext-1.1.1或者是ext-2.0下载。明显可以看出来ext-2.0的版本高,12月4日终于正式发布了,尚未经过详细测试,所以不敢说什么。下面我们把两个版本都介绍一点儿。
1.2. 看看ext-1.1.1的文档
docs目录下是api文档,基本上所有的函数,配置,事件都可以在里边找到,直接打开index.html就可以查看,左侧菜单还包含了对examples目录下例子的引用,不过有些例子需要使用json与后台做数据交换,必须放到服务器上才能看到效果。还有一些后台代码是使用php编写的,如果想看这些例子的效果,还需要配置php运行环境。
如果你用java,而且jdk在1.5以上,不如直接装个resin-3方便,它可以跑php呢。
1.3. 看看ext-2.0的文档
api文档依然在docs目录下,虽然可以看到左边的菜单,但是点击之后,右侧的api页面都是靠ajax获得的,所以不能直接在本地查看了,你必须把整个解压缩后的目录放到服务器上,通过浏览器访问服务器,才能看到。
问为什么docs打不开,只能看到一直loading的兄弟,都是因为没把这些东西放到服务器上的原因。
2.0中的api文档中没有例子的链接了,你需要自己去examples目录下找你需要的例子,然后打开看,当然还是有一些例子需要放在服务器上才能看到效果。
1.4. 为什么有的例子必须放在服务器上才能看到效果?
因为有些例子里,用到Ajax去后台读取数据,如果没在服务器上,Ajax返回的状态一直是失败,也无法获得任何数据,所有就看不到正确的效果。不过以前在extjs.com论坛上看到过有人写了localXHR,可以让ajax从本地文件系统获得数据,这样也许就可以摆脱服务器的束缚了。
1.5. 为什么自己按照例子写的代码,显示出来总找不到图片
ext里经常用一个空白图片做占位符号,然后用css里配置的背景图片做显示,这样有利于更换主题。这个空白图片的名字就是Ext.BLANK_IMAGE_URL,默认情况下它的值是BLANK_IMAGE_URL : "http:/"+"/extjs.com/s.gif"。虽然图片很小,也要去网上下载,一旦下载失败就显示找不到图片了。
看到这里可能有人奇怪了,为什么examples下的例子没有找不到图片的问题呢?看来你没有仔细看例子那些代码呢,每个例子都引用了../examples.js。在examples.js里设置了Ext.BLANK_IMAGE_URL = '../../resources/images/default/s.gif';。所以要解决自己写的例子找不到图片的问题,只需要照examples.js里修改s.gif的本地路径就可以了。很简单吧?
1.6. 我们还需要什么?
? 介于本人对firefox的喜爱,以及firebug在调试js过程中的便利,隆重向您推荐firefox+firebug的开发组合。再说了ext开发者也都是倾向于firefox开发的,所以一般都是在firefox上跑的好好的,放到ie上就出问题。这也跟ie自身的问题有些关系,可是目前ie占据90%的浏览器市场,最后我们还是需要让自己的项目在ie上跑起来,所以要求我们能写出跨浏览器的js来。
firebug的好处在于,可以显示动态生成的dom,你甚至可以在firebug里直接对dom进行修改,而这些修改会直接反应到显示上。太厉害了
firebug提供的console,可以直接执行js脚本,配置console.debug,console.info,console.error等日志方法更便于跟踪。
对于ajax发送接收的数据,firebug都可以显示出来,并且可以查看发送的参数,以及返回的状态和信息。
1.7. 入门之前,都看helloworld。
为了照顾连最基本应用都跑不起来的同志,我们给出两个入门版helloworld范例,并结合讲解,领你入门呢。
1.7.1. 直接使用下载的发布包
? 先去http://www.extjs.com/download下载zip格式的发布包
? 随便解压缩到什么目录下,不管目录名是什么,最后应该看到里边是这样的目录结构。

? 现在我们利用它的目录结构,写一个helloworld例子。
进入上图中的examples目录,新建一个helloworld目录,helloworld目录下新建一个helloworld.html文件,将下列内容复制进index.html文件中。
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="../examples.js"></script>

<script>
Ext.onReady(function(){
    Ext.MessageBox.alert('helloworld', 'Hello World.');
});
</script>
                   
? 双击helloworld.html打开页面,效果如下:

很高兴的告诉您,咱们的helloworld范例已经正确的执行了,下一步你最好把examples目录下的例子都看一看,再看看里边的代码怎么写的,好好感受一下ext的风范,再继续下去。
1.7.2. 只把必要的东西放进项目中
想把ext放入自己的项目,需要自己整理一下,因为发布包里的东西并非都是必要的,比如文档,比如例子,比如源代码。
必要的最小集合是这样:ext-all.js,adapter/ext/ext-base.js,build/locale/ext-lang-zh_CN.js和整个resources目录。
? ext-all.js,adapter/ext/ext-base.js就包含了ext的所有功能,所有的js脚本都在这里了。
? build/locale/ext-lang-zh_CN.js是中文翻译。
? resources目录下是css样式表和图片。
自己的项目里只需要包含这些东西,就可以使用ext了。使用时,在页面中导入:
<link rel="stylesheet" type="text/css" href="${放置ext的目录}/resources/css/ext-all.css" />
<script type="text/javascript" src="${放置ext的目录}/ext-base.js"></script>
<script type="text/javascript" src="${放置ext的目录}/ext-all.js"></script>
<script type="text/javascript" src="${放置ext的目录}/ext-lang-zh_CN.js"></script>
           
请注意js脚本的导入顺序,其他的就可以自由发挥了。

第2章 震撼吧!让你知道ext表格控件的厉害
2.1.功能丰富,无人能出其右
无论是界面之美,还是功能之强,ext的表格控件都高居榜首。
单选行,多选行,高亮显示选中的行,推拽改变列宽度,按列排序,这些基本功能咱们就不提了。
自动生成行号,支持checkbox全选,动态选择显示哪些列,支持本地以及远程分页,可以对单元格按照自己的想法进行渲染,这些也算可以想到的功能。
再加上可编辑grid,添加新行,删除一或多行,提示脏数据,推拽改变grid大小,grid之间推拽一或多行,甚至可以在tree和grid之间进行拖拽,啊,这些功能实在太神奇了。更令人惊叹的是,这些功能竟然都在ext表格控件里实现了。
呵呵~不过ext也不是万能的,与fins的ecside比较,ext不能锁定列(土豆说1.x里支持锁定列,但是2.0里没有了,因为影响效率。),也没有默认的统计功能,也不支持excel,pdf等导出数据。另外fins说,通过测试ecside的效率明显优于ext呢。:)
2.2. 让我们搞一个grid出来耍耍吧。
光说不练不是我们的传统,让我们基于examples里的例子,来自己搞一个grid看看效果,同时也可以知道一个grid到底需要配置些什么东西。
? 首先我们知道表格肯定是二维的,横着叫行,竖着叫列。跟设计数据库,新建表一样,我们要先设置这个表有几列,每列叫啥名字,啥类型,咋显示,这个表格的骨架也就出来了。
ext里,这个列的定义,叫做ColumnModel,简称cm的就是它,它作为整个表格的列模型,是要首先建立起来的。
这里我们建立一个三列的表格,第一列叫编号(code),第二列叫名称(name),第三列叫描述(descn)。
var cm = new Ext.grid.ColumnModel([
    {header:'编号',dataIndex:'id'},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
               
看到了吧?非常简单的定义了三列,每列的header表示这列的名称,dataIndex是跟后面的东西对应的,咱们暂且不提。现在只要知道有了三列就可以了。
? 有了表格的骨架,现在我们要向里边添加数据了。这个数据当然也是二维了,为了简便,我们学习examples里的array-grid.js里的方式,把数据直接写到js里。
? var data = [
?     ['1','name1','descn1'],
?     ['2','name2','descn2'],
?     ['3','name3','descn3'],
?     ['4','name4','descn4'],
?     ['5','name5','descn5']
? ];
               
很显然,我们这里定义了一个二维数据, (什么?你不知道这是二维数组?快改行吧,这里不是你该待的地方。)
这个有五条记录的二维数组,显示到grid里就应该是五行,每行三列,正好对应这id,name,descn,在我们的脑子里应该可以想像出grid显示的结果了,为了让想像变成显示,我们还需要对原始数据做一下转化。
? 因为咱们希望grid不只能支持array,还可以支持json,支持xml,甚至支持咱们自己定义的数据格式,ext为咱们提供了一个桥梁,Ext.data.Store,通过它我们可以把任何格式的数据转化成grid可以使用的形式,这样就不需要为每种数据格式写一个grid的实现了。现在咱们就来看看这个Ext.data.Store是如何转换array的。
? var ds = new Ext.data.Store({
?     proxy: new Ext.data.MemoryProxy(data),
?     reader: new Ext.data.ArrayReader({}, [
?         {name: 'id'},
?         {name: 'name'},
?         {name: 'descn'}
?     ])
? });
? ds.load();
               
ds要对应两个部分:proxy和reader。proxy告诉我们从哪里获得数据,reader告诉我们如何解析这个数据。
现在我们用的是Ext.data.MemoryProxy,它是专门用来解析js变量的。你可以看到,我们直接把data作为参数传递进去了。
Ext.data.ArrayReader专门用来解析数组,并且告诉我们它会按照定义的规范进行解析,每行读取三个数据,第一个叫id,第二个叫name,第三个descn。是不是有些眼熟,翻到前面cm定义的地方,哦,原来跟dataIndex是对应的。这样cm就知道哪列应该显示那条数据了。唉,你要是能看明白这一点,那你实在是太聪明了。
记得要执行一次ds.load(),对数据进行初始化。
有兄弟可能要问了,要是我第一列数据不是id而是name,第二列数据不是name而是id咋办?嗯,嗯,这个使用就用mapping来解决。改改变成这样:
var ds = new Ext.data.Store({
    proxy: new Ext.data.MemoryProxy(data),
    reader: new Ext.data.ArrayReader({}, [
        {name: 'id', mapping: 1},
        {name: 'name', mapping: 0},
        {name: 'descn', mapping: 2}
    ])
});
               

这样如截图所见,id和name两列的数据翻转了。如此这般,无论数据排列顺序如何,我们都可以使用mapping来控制对应关系,唯一需要注意的是,索引是从0开始的,所以对应第一列要写成mapping:0,以此类推。
? 哈哈,万事俱备只欠东风,表格的列模型定义好了,原始数据和数据的转换都做好了,剩下的只需要装配在一起,我们的grid就出来了。
? var grid = new Ext.grid.Grid('grid', {
?     ds: ds,
?     cm: cm
? });
? grid.render();
               
注意:上头是ext-1.x的写法,Ext.grid.Grid的第一个参数是渲染的id,对应在html里应该有一个 <div id="grid"></div>的东西,这样grid才知道要把自己画到哪里。
创建完grid以后,还要用grid.render()方法,让grid开始渲染,这样才能显示出来。
? 好了,把所有代码组合到一起,看看效果吧。
?     var cm = new Ext.grid.ColumnModel([
?         {header:'编号',dataIndex:'id'},
?         {header:'名称',dataIndex:'name'},
?         {header:'描述',dataIndex:'descn'}
?     ]);
?
?     var data = [
?         ['1','name1','descn1'],
?         ['2','name2','descn2'],
?         ['3','name3','descn3'],
?         ['4','name4','descn4'],
?         ['5','name5','descn5']
?     ];
?
?     var ds = new Ext.data.Store({
?         proxy: new Ext.data.MemoryProxy(data),
?         reader: new Ext.data.ArrayReader({}, [
?             {name: 'id'},
?             {name: 'name'},
?             {name: 'descn'}
?         ])
?     });
?     ds.load();
?
?     var grid = new Ext.grid.Grid('grid', {
?         ds: ds,
?         cm: cm
?     });
?     grid.render();
?
               
看看吧,这就是咱们搞出来的grid了。

html例子是lingo-sample/1.1.1目录下的02-01.html,把这个目录copy到ext-1.x的example目录下,就可以直接打开观看效果。
2.3.上边那个是1.x的,2.0稍微有些不同哦
首先,Ext.grid.Grid已经不见了,咱们需要用Ext.grid.GridPanel。需要传递的参数也有少许区别。
var grid = new Ext.grid.GridPanel({
    el: 'grid',
    ds: ds,
    cm: cm
});
       
看到了吗?负责指定渲染位置的id放到了{}里边,对应的名字是el。似乎ext2里对这些参数进行了统一,比以前更整齐了。
因为其他地方都一样,我就不多说了,html例子在是lingo-sample/2.0目录下的02-01.html。

从截图上看,少了斑马条,下边多了一条线,应该只是css有所不同吧。
默认情况下,两个版本的grid都可以拖拽列,也可以改变列的宽度。不知道怎么禁用这两个功能呢。


最大的不同应该是1.x里默认支持的右键效果,在2.0里不见了。

按shift和ctrl多选行的功能倒是都有。区别是,全选后,1.x必须按住ctrl才能取消,直接单击其中一个行,不会取消全选功能,而2.0里只需要任意点击一行,就取消全选,只选中刚才点击的那行。


哦,哦,那颜色不要也算是区别吧。
2.4.按顺序,咱们先要把常见功能讲到,让grid支持按列排序
其实很简单,需要小小改动一下列模型。
var cm = new Ext.grid.ColumnModel([
    {header:'编号',dataIndex:'id',sortable:true},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
       
如果你英语还可以,或者懂得查字典的话(软件翻译也算),那么你就会知道,多出来的这个sortable属性应该是可以排序的意思。现在咱们试一下改动后的效果。


看到了没有?编号的标题上有个小小的箭头,表格里的数据也是按照编号做的逆序排列,如此简单,我们就实现了按列排序。
很有趣的是,2.0加上sortable以后,1.x那种右键功能也跑回来了,不过它用的不是右键,而是下拉菜单似的实现方式。

什么?你问为什么其他两列无法排序?!嗯,好像是因为你还没有给另两列添加sortable属性。
怎么加?!按编号那样加就行了。
还是不会?!-_-。
var cm = new Ext.grid.ColumnModel([
    {header:'编号',dataIndex:'id',sortable:true},
    {header:'名称',dataIndex:'name',sortable:true},
    {header:'描述',dataIndex:'descn',sortable:true}
]);
       
这样所有列都可以排序了。什么?怎么取消排序?!-_-。
2.5.让单元格里显示红色的字,图片,按钮,你还能想到什么?
嘿,希望你跟我一样,不愿意只能在grid里看到文字,至少不是单调的,毫无特色的文字。有些人就问了,如果我想改变一下单元格里显示内容,应该怎么办呢?
非常不幸的是,ext的作者,伟大的jack早已经想到了,说真的,你没想到的,他都想到了,不只想到了,他还做出来了。
唉,这就是区别啊。为啥你就不能动手做些东西呢?就知道向别人要这要那,唉。
首先,我宣布,偶们的数据要扩充啦,每个人要加上一个性别字段。
var data = [
    ['1','male','name1','descn1'],
    ['2','female','name2','descn2'],
    ['3','male','name3','descn3'],
    ['4','female','name4','descn4'],
    ['5','male','name5','descn5']
];
       
男女搭配,干活不累撒。而且现在中国就是男多女少,我就还没对象呢。征婚中,单身女性加(QQ)85533441详谈。
你可以试试不改其他的部分,显示的结果是不会改变的,因为原始数据要经过ds的处理才能被grid使用,那么下一步我们就开始修改ds,把性别加进去。
var ds = new Ext.data.Store({
    proxy: new Ext.data.MemoryProxy(data),
    reader: new Ext.data.ArrayReader({}, [
        {name: 'id'},
        {name: 'sex'},
        {name: 'name'},
        {name: 'descn'}
    ])
});
       
添加了一行{name: 'sex'},把数组的第二列映射为性别。现在grid可以感觉到sex了,嘿嘿。
不过grid还显示不了性别这列,因为咱们还没改cm。
var cm = new Ext.grid.ColumnModel([
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex'},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
       
到现在其实都没什么新东西,但是你不觉得光看平板字,很难分出哪个是GG哪个是MM吗?听说过红男绿女没?要是男的都加上红色,女的都变成绿色,那不是清楚多了。就像下面一样。

怎么样?是不是效果大不同了。你不会认为很难吧,嗯,确实,如果你对html和css完全搞不明白的话,劝你还是先去学学吧,对自己有信心的往下看。
var cm = new Ext.grid.ColumnModel([
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex',renderer:function(value){
            if (value == 'male') {
                return "<span style='color:red;font-weight:bold;'>红男</span>";
            } else {
                return "<span style='color:green;font-weight:bold;'>绿女</span>";
            }
        }},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
       
别被吓到,这么一大段其实就是判断是男是女,然后配上颜色。你要是觉得乱,也可以这么做。
function renderSex(value) {
    if (value == 'male') {
        return "<span style='color:red;font-weight:bold;'>红男</span>";
    } else {
        return "<span style='color:green;font-weight:bold;'>绿女</span>";
    }
}
var cm = new Ext.grid.ColumnModel([
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex',renderer:renderSex},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
       
实际上这个renderer属性至关重要,它的值是一个function,哦,你说不知道js里function可以这么用?那么恭喜你,现在你知道了。
renderer会传递个参数进去,咱们grid里看到的,是这个函数的返回值,怎么样,神奇吧?
同志们,你们也应该看到了,返回html就可以,是html啊,html里有的东西,你返回什么就显示什么,颜色,链接,图片,按钮,只要你愿意,整个网页都可以返回回去。还有什么做不到的?哦,你不会html,那没辙,回去学吧。
咱们先来个图片。

function renderSex(value) {
    if (value == 'male') {
        return "<span style='color:red;font-weight:bold;'>红男</span><img src='user_male.png' />";
    } else {
        return "<span style='color:green;font-weight:bold;'>绿女</span><img src='user_female.png' />";
    }
}
       
是不是太简单了,下面咱们来玩点儿高级的。
function renderDescn(value, cellmeta, record, rowIndex, columnIndex, store) {
    var str = "<input type='button' value='查看详细信息' onclick='alert(\"" +
        "这个单元格的值是:" + value + "\\n" +
        "这个单元格的配置是:{cellId:" + cellmeta.cellId + ",id:" + cellmeta.id + ",css:" + cellmeta.css + "}\\n" +
        "这个单元格对应行的record是:" + record + ",一行的数据都在里边\\n" +
        "这是第" + rowIndex + "行\\n" +
        "这是第" + columnIndex + "列\\n" +
        "这个表格对应的Ext.data.Store在这里:" + store + ",随便用吧。" +
        "\")'>";
    return str;
}
       
来看看我们可以在render里用到多少参数:
? value是当前单元格的值
? cellmeta里保存的是cellId单元格id,id不知道是干啥的,似乎是列号,css是这个单元格的css样式。
? record是这行的所有数据,你想要什么,record.data["id"]这样就获得了。
? rowIndex是行号,不是从头往下数的意思,而是计算了分页以后的结果。
? columnIndex列号太简单了。
? store,这个厉害,实际上这个是你构造表格时候传递的ds,也就是说表格里所有的数据,你都可以随便调用,唉,太厉害了。
有个同学就问啦:EXT render的参数,是如何得到的呢。因为你讲的那些都是EXT自己内部的。它是如何把这些参数传递给render的呢?
这个问题其实比较简单,只是你们想复杂了。既然是函数,就肯定有调用它的地方,你找到GridView.js在里边搜索一下renderer,就会看到调用render的地方,这些参数都是在这里传进去的。
好,看看效果吧。


剩下的,就是发挥各位聪明才智的时候了,舞台已经搭好,看你如何表演了。
html例子,1.x版本在lingo-sample/1.1.1目录下的02-02.html,2.0的版本在lingo-sample/2.0目录下的02-02.html。
2.6.更进一步,自动行号和多选checkbox
实际上行号和多选checkbox都是renderer的延伸,当然多选checkbox更酷一点儿,两者经常一起使用,所以让我们放在一起讨论好了。
2.6.1. 自动行号
只需要在cm中加上一行,这一行不会与ds中的任何数据对应,这也告诉我们可以凭空制作列,哈哈。
在之前的例子上改啦。
var cm = new Ext.grid.ColumnModel([
    {header:'NO.',renderer:function(value, cellmeta, record, rowIndex){
        return rowIndex + 1;
    }},
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex'},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
           
如吾等所愿,不指定dataIndex,而是直接根据renderer返回rowIndex + 1,因为它是从0开始的,所以加个一。截图如下。

1.x的例子在lingo-sample/1.1.1/02-03.html
很遗憾的是,2.0里有自己的默认实现了,咱们不能展现自己的手工技艺了,还是乖乖使用jack提供的东东吧。
于是,在2.0里cm就变成了这幅模样。
var cm = new Ext.grid.ColumnModel([
    new Ext.grid.RowNumberer(),
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex'},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
           
你绝对会同意我的意见,Jack's work is amazing.实在是太神奇了。看看截图就知道。

2.0的例子在lingo-sample/2.0/02-03.html
2.6.2. 全选checkbox的时间了,请允许我让2.0先上场。
因为2.0里有checkboxSelectionModel,这样完全可以证实用别人的轮子,比自己造轮子要方便。而且咱们造的轮子完全没有jack圆。不信的话,看下面1.x里的实现。
我们一直在修改cm,这次我们也要对它动刀了,不过SelectionModel既sm也要处理一下,这是为了改变单选和多选行的方式,以前这些可以靠shift或ctrl实现,而现在这些都要与checkbox关联上了。
啦啦啦,先看图片,后看代码。

先看看具体多了什么
var sm = new Ext.grid.CheckboxSelectionModel();
           
神奇的是这个sm身兼两职,使用的时候既要放到cm里,也要放到grid中。代码如下。
var cm = new Ext.grid.ColumnModel([
    new Ext.grid.RowNumberer(),
    sm,
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex'},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);

var grid = new Ext.grid.GridPanel({
    el: 'grid',
    ds: ds,
    cm: cm,
    sm: sm
});
           
然后你就可以得到效果啦,代码在lingo-sample/2.0/02-04.html。
2.6.3. 1.x时代的全选checkbox。
理论上只需要给cm再加一列,像自动编号那样,不对应数据,只显示checkbox就可以了。难点就是checkbox与某一行被选择的时候要对应上,用checkbox选择上,sm里也要让这一行被选中,反之亦然。嗯,估计会比较复杂呢。
先放上显示checkbox的代码和截图:
var cm = new Ext.grid.ColumnModel([
    {header:'NO.',renderer:function(value, cellmeta, record, rowIndex){
        return rowIndex + 1;
    }},
    {header:'<input type="checkbox" onclick="selectAll(this)">',renderer:function(value, cellmeta, record, rowIndex){
        return '<input type="checkbox" name="cb">';
    }},
    {header:'编号',dataIndex:'id'},
    {header:'性别',dataIndex:'sex'},
    {header:'名称',dataIndex:'name'},
    {header:'描述',dataIndex:'descn'}
]);
           

与sm对接的方面比较麻烦,好在extjs.com上已经有扩展了,或者你可以看看我们弄下来的。 看看1.x多选树的截图。
2.7.分页了吗?分页了吗?如果还没分就看这里吧。
如果你有一千条信息,一次都输出到grid里,然后让客户用下拉条一点儿一点儿去找吧。我们这里可是要做一个分页效果了,不用滚动屏幕,或者滚动一下就可以看到本页显示的数据,如果想看其他的只需要翻页就可以了。同志们,加强客户体验呀。
实际上,grid控件挺耗性能的,据土豆讲一个页面上放3个grid就可以感觉到响应变慢,以前看过介绍,grid里显示数据过多,听说是上千条,也会明显变慢。
所以说分页是必不可少滴,而且jack提供了方便集成分页工具条的方式,不用一下实在是太浪费了。
两步走,让grid集成分页。
2.7.1. 表面工作,先把分页工具条弄出来。

从图片可以清晰的看到,在grid下边多出来一行东东,包括了前一页,后一页,第一页,最后一页,刷新,以及提示信息。而我们不过写了如下几行代码而已,神奇呀。
var gridFoot = grid.getView().getFooterPanel(true);

var paging = new Ext.PagingToolbar(gridFoot, ds, {
    pageSize: 10,
    displayInfo: true,
    displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
    emptyMsg: '没有记录'
});
           
首先使用grid.getView().getFootPanel(true),获得grid下边那一条,嘿嘿,人家grid就设计的这么好,脚底下专门留了地方让你放东西。我们老实不客气,把Ext.PagingToolbar放到上边,就可以显示分页工具条了。
这分页工具条可不是个摆设,你按了前一页,后一页,整个grid都要有反应才对,要不咱们费劲弄这个么东西过来干嘛呀?所以,我们在构造PagingToolbar的时候,不但告诉它,在gridFoot上显示,还告诉它,进行分页跳转的时候,要对ds也进行操作。这个ds就是grid用来获取和显示数据的,它一变整个grid都发生变化,嘿嘿~这就算共享数据模型了。厉害呀。
知道了分页的玄机,让我们揉揉眼睛,好好看看里边的参数。
? pageSize,是每页显示几条数据。
? displayInfo,跟下面的配置有关,如果是false就不会显示提示信息。
? displayMsg,只有在displayInfo:true的时候才有效,用来显示有数据的时候的提示信息,中国人应该看得懂汉语,到时候{0},{1},{2}会自动变成对应的数据,咱们只需要想办法把话说通就行了。
? emptyMsg,要是没数据就显示这个,jack实在太贴心了,连这些小处都考虑得如此精细。
最好要注意的是,这段代码必须放在grid.render()之后,估计是因为动态生成grid的dom后,才能获得对应的footPanel。
现在给出例子,1.x的例子在lingo-sample/1.1.1/02-05.html。
2.7.2. 2.0赐予我们更大的灵活性
其实,在下,一直,对于:必须先渲染grid才能获得footPanel这事非常愤恨,你想啊,本来分页也应该属于初始化的一部分,现在却要先初始化grid,配置完毕,渲染,回过头来再从grid里把拿出来footPanel,再咕哝分页的配置。真,真,真郁闷呀。
所以2.0里的方式,简直大快民心。
var grid = new Ext.grid.GridPanel({
    el: 'grid',
    ds: ds,
    cm: cm,
    bbar: new Ext.PagingToolbar({
        pageSize: 10,
        store: ds,
        displayInfo: true,
        displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
        emptyMsg: "没有记录"
    })
});
ds.load();
           
嘿嘿,加一个bbar的参数就可以了,bbar就是bottom bar啦,底端工具条。

不过还是要注意一点,与1.x中不同的是,如果配置了分页工具条,ds.load()就必须在构造grid以后才能执行,否则分页工具条会不起作用。看来分页工具条会把自己和ds做一些关联,来完成与grid共享数据模型的。
对了,还有一点不同,1.x中分页条不会随着浏览器的大小改变,自动放缩,这点在2.0中也解决了。
2.0的例子在lingo-sample/2.0/02-05.html。
2.7.3. 迫不得已,要加上后台脚本了。
grid会每次都显示ds中所有的数据,咱们没法利用静态数据好好演示分页,于是,必须写后台脚本,让ext与后台进行数据交互才能看到真实的分页效果。
咱们尽量在原来的基础上修改啊,把注意力集中在关键部位,一次性突破。
我不会其他语言,后台就用jsp写啦。有个好消息是只要返回的数据格式一样,ext才不管后台是什么写的呢。也就是这样,不管你以后用什么实现后台,前台的ext代码都一样。
<%
String start = request.getParameter("start");
String limit = request.getParameter("limit");
try {
    int index = Integer.parseInt(start);
    int pageSize = Integer.parseInt(limit);

    String json = "{totalProperty:100,root:[";
    for (int i = index; i < pageSize + index; i++) {
        json += "{id:" + i + ",name:'name" + i + "',descn:'descn" + i + "'}";
        if (i != pageSize + index - 1) {
            json += ",";
        }
    }
    json += "]}";
    response.getWriter().write(json);
} catch(Exception ex) {
}
%>
           
下面我们来解读这段jsp代码:
? 在进行操作之前,我们先要获得ext传递过来的两个参数:start和limit,start指的从第几个数据开始显示,limit是说从start开始,一共要用多少个数据,当然返回的数据可能会小于这个值。
? 咱们在后台模拟对100条数据进行分页,在获得了start和limit之后再生成json格式的数据。
何谓json?在理论讲解之前先看看实例,让我们能有个感性认识,至于以后能不能升华到理性认识,就看各位的悟性了。
模拟ext访问后台,并传递两个参数start=0&limit=10,把获得的数据稍微整理一下,是这个样子。
{totalProperty:100,root:[
    {id:0,name:'name0',descn:'descn0'},
    {id:1,name:'name1',descn:'descn1'},
    {id:2,name:'name2',descn:'descn2'},
    {id:3,name:'name3',descn:'descn3'},
    {id:4,name:'name4',descn:'descn4'},
    {id:5,name:'name5',descn:'descn5'},
    {id:6,name:'name6',descn:'descn6'},
    {id:7,name:'name7',descn:'descn7'},
    {id:8,name:'name8',descn:'descn8'},
    {id:9,name:'name9',descn:'descn9'}
]}
                   
请记住这个数据格式,不管后台是什么,只要满足了这样的格式要求,ext就可以接收处理,显示到grid中。
我这里就不好好介绍json,现在只需要知道json里头除了name(名称)就是value(值),值有好几种格式,如果是数字就不用加引号,如果加了引号就是字符串,如果用[]包裹就是数组,如果出现{}就说明是嵌套的json。诸如此类。
简单瞄了json一眼,开头就是totalProperty:100,这告诉ext:“俺这里有100个数据呢。”,然后就是root:[],root对应着一个数组,数组里有10个对象,每个对象都有id呀,name呀,descn呀。这10个数据最后就应该显示到表格里。
? jsp里用for循环生成root数组里的数据,这样我们翻页的时候可以看到数据的变化,要不每次都是一样的数据,你怎么知道翻页是不是起作用了呢?
最后我们把得到的json字符串输出到response里,ext也就可以获得这些数据了。
结果经过了一番折腾,我们的jsp已经确定可以返回我们所需要的数据了。现在我们可以忘掉后台是用什么语言写的了,直接切入ext代码,看看它是怎么样才能跑去后台获得这些数据呢?
因为引入了json作为数据传输格式,这次我们要对ext代码进行一次大换血了,请做好思想准备。
? 换掉proxy,别在内存里找了,让我们通过http获得我们想要的。
? proxy: new Ext.data.HttpProxy({url:'grid.jsp'}),
                   
创建HttpProxy的同时,用url这个参数指定咱们去哪里取数据,我们这里设置成grid.jsp,就是我们刚才讨论的jsp脚本啦。
? 已经不是数组了,现在要用json咯。
? reader: new Ext.data.JsonReader({
?     totalProperty: 'totalProperty',
?     root: 'root'
? }, [
?     {name: 'id'},
?     {name: 'name'},
?     {name: 'descn'}
? ])
                   
看比ArrayReader多了什么?totalProperty对应咱们jsp返回的totalProperty,也就是数据的总数。root对应咱们jsp返回的root,就是一个包含返回数据的数组。
? 好了,最后在初始化的时候,告诉我们希望获得哪部分的数据就可以了。
? ds.load({params:{start:0,limit:10}});
                   
就在ds读取的时候添加两个参数,start和limit,告诉后台,我们从第一个数可以取起,最多要10个。
在这里有一个小插曲,如果你按照我们以前的设置,grid是无法正常显示的,因为ds.load()无法在grid.render()前准备好所有数组,所以它不知道自己应该实现多高。没法子,我们只好为它指定一个固定的高度了。像这样<div id="grid" style="height:265px;"></div>。
最后,我们就可以使用分页条上那些按钮试试分页了。呵呵~就这么简单。
1.x的例子在lingo-sample/1.1.1/02-06.html,jsp文件在lingo-sample/1.1.1/grid.jsp,下面是它的截图:

有趣的是,1.x中,不需要为div指定高度,它自己就知道如何显示,不晓得为什么2.0里不这样做呢。
2.0的例子在lingo-sample/2.0/02-06.html,jsp文件在lingo-sample/2.0/grid.jsp,下面是它的截图:

2.7.4. 其实分页不一定要踩在脚下,也可以顶在头上。
我的意思是,grid除了FootPanel以外,还有HeaderPanel,意思就是头顶上的面板。我们把分页条放在上面也不会有任何问题。1.x中的代码仅仅是将getFooterPanel改成getHeaderPanel,这样分页条就跑到上头去了。
var gridHead = grid.getView().getHeaderPanel(true);

var paging = new Ext.PagingToolbar(gridHead, ds, {
    pageSize: 10,
    displayInfo: true,
    displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
    emptyMsg: '没有记录'
});
           

1.x的例子可以在lingo-sample/1.1.1/02-07.html找到。
2.0就更简单了,只需要改一个字母,b -> t,呵呵~,让原来的bbar(bottom bar)变成tbar(top bar)就可以让我们的工具条登天了。
var grid = new Ext.grid.GridPanel({
    el: 'grid',
    ds: ds,
    cm: cm,
    tbar: new Ext.PagingToolbar({
        pageSize: 10,
        store: ds,
        displayInfo: true,
        displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
        emptyMsg: "没有记录"
    })
});
           

2.0的例子可以在lingo-sample/2.0/02-07.html找到。
呃,当然你可以让上下都加上分页条,反正它们最后都是共享一个ds,功能不会有任何问题哈。吼吼。
2.8. 可编辑表格,改变大小,表格间拖拽,树与表格间拖拽。

第 3 章 歌颂吧!只为了树也要学ext。
上一页
下一页

________________________________________
第3章 歌颂吧!只为了树也要学ext
3.1.真的,我是为了树,才开始学ext的
之前使用过xtree和dojo中的tree,感觉都是怪怪的,界面简陋,功能也不好上手,待看到ext里的树形真是眼前一亮,在此之前,动态增添,修改删除节点,拖拽和右键菜单,我一直认为是不可能实现的任务,而在ext上却轻松实现了,而且界面和动画效果相当完美。真是让人爱不释手啊。
树形是非常典型的一种数据结构,多级菜单,部门组织结构,省市县三级这种金字塔结构都可以用树形表示,要表示一个老爸有一帮孩子的情况真是非树形莫属啊,做好了这部分,绝对是个亮点。
3.2. 传统是先做出一棵树来。
树形世界的万物之初是一个TreePanel。
var tree = new Ext.tree.TreePanel('tree');
       
这里的参数'tree',表示渲染的dom的id。html写着个<div id="tree"></div>做呼应呢,最后这棵树就出现在这个div的位置上。
现在我们获得了树形面板,既然是树就必须有一个根,有了根才能在上边加枝子,放叶子,最后装饰的像一棵树似的。嗯,所以根是必要的,我们就研究研究这个根是怎么咕哝出来的。
var root = new Ext.tree.TreeNode({text:'偶是根'});
       
看到了吧,它自己都说它自己是根了,所以它就肯定是根没错。再看下面。
tree.setRootNode(root);
tree.render();
       
首先,我们把这个根root,放到tree里,用了setRootNode()方法,就是告诉tree,这是一个根,你可得把它放好啊。
立刻对tree进行渲染,让它出现在id="tree"的地方,这个id可是在上面指定的,如果有疑问,请翻回去继续研究,我们就不等你,继续了。
当当,我非常荣幸的向您宣布,咱们的第一棵树出来了。这是它的照片。

靠,这是树吗?
嗯,现在的确不太像树,因为它只有一个根。
靠,我们要的是树,你就搞出一个根来,有鸟用啊。
嗯,理论上等它自己发芽长成树,可能性是比较小,不如咱们偷偷插上几个枝子,说不定看起来就更像树了。
注意
虽然只有一个根,不过也算是棵树,1.x的例子在lingo-sample/1.1.1/03-01.html。
3.3. 超越一个根
上回书说道,我们要偷偷插上几个杈子,让这个本来就是树的树,更像一棵树。
var root = new Ext.tree.TreeNode({text:'偶是根'});
var node1 = new Ext.tree.TreeNode({text:'偶是根的第一个枝子'});
var node2 = new Ext.tree.TreeNode({text:'偶是根的第一个枝子的第一个叶子'});
var node3 = new Ext.tree.TreeNode({text:'偶是根的第一个叶子'});
node1.appendChild(node2);
root.appendChild(node1);
root.appendChild(node3);
       
我们从外边拿来三个TreeNode,不管不顾就把node2插到node1上,node1,node2一起插到root上,这下好了,咱们的树立刻就像是一棵树了。

咦?它怎么还是这么点儿东西?客官莫急,待小二儿给你整治一番,即看庐山真面目。

嗯,现在的确有点儿意思了,不过它开始的时候就那么缩在一团,看着很不爽,每次都要点这么几下才能看到底下的东西,咱们有没有办法让它每次渲染好就自己展开呢?
方法当然有咯,请上眼。
root.expand(true, true);
       
这一下就能解您燃眉之急,第一个参数是说,是否递归展开所有子节点,如果是false,就只展开第一级子节点,子节点下面还是折叠着的,。第二个参数是说是否有动画效果,true的话,你可以明显看出来那些节点是一点儿点儿展开的,否则就是刷拉一下子就出来了.
为了方便,咱们的例子里直接就可以展开了,省的再去点啊。1.x的例子在lingo-sample/1.1.1/03-02.html。
3.4. 你不会认为2.0里跟1.x是一样的吧?
第一个区别就是TreePanel的定义,原来的id要放到{}中,对应的名字是el。像这样改:
var tree = new Ext.tree.TreePanel({
    el:'tree'
});
       
即使这样改完了,还是什么都看不见,我们错过了什么?用findbug看了一下dom,height竟然是0,当然啥也看不见了,2.0里的树为啥不会自动伸缩呢,只好咱们给它设置一个初始高度,在html里设置个300px的高度,就可以显示出来了。
<div id="tree" style="height:300px;"></div>
       
另一个也如法炮制。我们就可以看到2.0比1.x多了鼠标移到树节点上时的高亮显示。

好了,看了这些例子,应该对树型有些认识了,虽然这里只有TreeNode,却能表示枝杈或者叶子,原理很简单,如果这个TreeNode下有其他节点,它就是一个枝杈,如果没有,它就是一个叶子,从它前头的图标就很容易看出来。嘿嘿,根其实就是一个没有上级节点的枝杈了。实际上,他们都是TreeNode而已,属性不同而已。
2.0的例子在lingo-sample/2.0/目录下,分别是03-01.html和03-02.html。
3.5. 这种装配树节点的形式,真是让人头大。
如此刀耕火种不但麻烦,而且容易犯错,有没有更简便一些的方法吗?答案是利用Ext.tree.TreeLoader和后台进行数据交换,我们在只提供数据,让TreeLoader帮咱们做数据转换和装配节点的操作。
啦啦啦,json和ajax要登场了,不过你是否还记得我说过,一旦涉及到ajax就需要配合服务器了,ajax是无法从本地文件系统直接取得数据的。
首先,让我们为TreePanel加上TreeLoader
var tree = new Ext.tree.TreePanel('tree', {
    loader: new Ext.tree.TreeLoader({dataUrl: '03-03.txt'})
});
       
在此,TreeLoader仅包含一个参数dataUrl: '03-03.txt',这个dataUrl表示,在渲染后要去哪里读取数据,为了方便模拟,我们写了一个txt文本文件提供数据,直接打开03-03.txt可以看到里边的内容。
[
    {text:'not leaf'},
    {text:'is leaf',leaf:true}
]
       
里边是一个包含了两个节点定义的数组,可能你会发觉那个多出来的属性leaf:true,它的效果很神奇,这一点我们马上就可以看到。
如果你现在就去匆匆忙忙的刷新页面,想看一下咱们的成果,那一定会失望而归,页面上没有像你期待的那样,从03-03.txt读取数据,显示到页面上,你依然只能看到那个孤零零的根。这是因为TreeNode是不支持ajax的,我们需要把根节点换成AsyncTreeNode,它可以实现咱们的愿望。
var root = new Ext.tree.AsyncTreeNode({text:'偶是根'});
       

估计谁第一次看到这场面都一定吓傻了。我们不是只定义了两个节点吗?怎么一下子跑出这么多东西来?先别着急,让我们先把root.expand(true, true)改成root.expand(),避免节点无限展开下去,然后慢慢研究这个情况。

现在场面被控制住了,取消了递归展开,只展开根节点的第一层节点,我们得到的确实是与03-03.txt文件里相对应的两个节点,不过这两个节点有些不同,not leaf节点的图标赫然是枝杈的图标,如果点击它前面的加号,便又成了上面的场景。Why?
原因就来自AsyncTreeNode,这个东西会继承根节点TreeLoader中dataUrl,你点展开的时候,会执行这个节点的expand()方法,ajax会跑到dataUrl指定的地址去取数据,用firebug可以看到当前节点id会作为参数传递给dataUrl指定的地址,这样我们的后台就可以通过这个节点的id计算出该返回什么数据,得到了数据TreeLoader去解析数据并装配成子节点,然后显示出来。
哈哈,现在就是关键部分了。因为咱们使用的03-03.txt提供的数据,不会判断当前节点的id,所以每次返回的数据都是一样的,这就是树会无限循环下去的原因了。
那么为啥只有第一个节点会无限循环下去呢?第二个节点就没有那个小加号,呵呵~因为第二个节点不是AsyncTreeNode ,TreeLoader在生成节点的时候会判断数据里的leaf属性,如果是leaf:true,那么就会生成TreeNode而不是AsyncTreeNode,TreeNode可不会自动去用ajax取值,自然就不会无限循环展开了。
现实中,异步读取属性的节点是很爽的一件事情,因为你可能要保存成千上万条节点记录。一次性全部装载的话,无论读取和渲染的速度都会很慢。使用异步读取的方式,只有点击某一节点的时候,才去获得子节点属性,并进行渲染,极大的提高了用户体验。而且ext的树形本身有缓存机制,打开一次,再点击也不会去重复读取了,提升了响应速度。
html例子,1.x版本在lingo-sample/1.1.1/03-03.html,2.0版本在lingo-sample/2.0/03-03.html。
为了巩固学习效果,咱们再写一个json获得数据的例子,这次的json稍微写复杂一点儿。

这次对应的json数据文件是03-04.txt。
[
    {text:'01',children:[
        {text:'01-01',leaf:true},
        {text:'01-02',children:[
            {text:'01-02-01',leaf:true},
            {text:'01-02-02',leaf:true}
        ]},
        {text:'01-03',leaf:true}
    ]},
    {text:'02',leaf:true}
]
       
这也可以看作是在数据不多的情况下,一次加载所有数据的途径,只要确保所有叶子节点上都加上leaf:true的属性,就不会出现循环展开的问题了。
html例子:1.x在lingo-sample/1.1.1/03-04.html,2.0在lingo-sample/2.0/03-04.html。
3.6. jsp的例子是一定要有的
3.7. 让你知道树都可以做些什么
3.7.1. 检阅树形的事件
3.7.2. 右键菜单并非单纯的事件
3.7.3. 默认图标好单调,改一下撒
3.7.4. 小小提示
3.8. 灰壳显灵!让我直接修改树节点的名称吧!
3.9. 我拖,我拖,我拖拖拖。
3.10. 更深一步,整合起来的是完整树形操作。


第4章 祝福吧!把表单和输入控件都改成ext的样式
4.1.不用ext的form啊,不怕错过有趣的东西吗?
初看那些输入控件,其实就是修改了css样式表而已。你打开firebug看看dom,确实也是如此,从这点看来,似乎没有刻意去使用ext的必要,诚然,如果单单要一个输入框,不管添入什么数据,就点击发送到后台,的确是不需要ext呢。
你不想用一些默认的数据校验吗?你不想在数据校验失败的时候,有一些突出的提示效果吗?你不想要超炫的下拉列表combox吗?你不想要一些你做梦才能朦胧看到的选择控件吗?唉,要是你也像我一样禁不起诱惑,劝你还是随着欲望的节拍,试一下ext的form和输入控件。
4.2. 慢慢来,先建一个form再说
var form = new Ext.form.Form({
    labelAlign: 'right',
    labelWidth: 50
});
form.add(new Ext.form.TextField({
    fieldLabel: '文本框'
}));
form.addButton("按钮");
form.render("form");
       

简单来说,就是构造了一个form,然后在里边放一个TextField,再放一个按钮,最后执行渲染命令,在id="form"的地方画出form和里边包含的所有输入框和按钮来。刷拉一下就都出来了。
不过即使这样,圆角边框可不是form自带的,稍稍做一下处理,参见html里的写法。
<div style="width:220px;margin-left:0px;">
    <div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>
    <div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc">
        <h3 style="margin-bottom:5px;">form</h3>
        <div id="form"></div>
    </div></div></div>
    <div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>
</div>
<div class="x-form-clear"></div>
       
开头结尾那些div就是建立圆角的,有了这些我们都可以在任何地方使用这种圆角形式了,不限于form哟。
html例子,1.x在lingo-sample/1.1.1/04-01.html
2.0里的FormPanel跟1.x里已经基本完全不一样了,咱们先看个简单例子:

代码如下:
var form = new Ext.form.FormPanel({
    defaultType: 'textfield',
    labelAlign: 'right',
    title: 'form',
    labelWidth: 50,
    frame: true,
    width: 220,

    items: [{
        fieldLabel: '文本框'
    }],
    buttons: [{
        text: '按钮'
    }]
});
form.render("form");
       
html里不需要那么多东西了,只需要定义一个div id="form"就可以实现这一切。明显可以感觉到初始配置更紧凑,利用items和buttons指定包含的控件和按钮。
现在先感觉一下,我们后面再仔细研究,例子见lingo-sample/2.0/04-01.html。
4.3. 胡乱扫一下输入控件
兄弟们应该都有html开发的经验了,像什么input用的不在少数了,所以咱们在这里也不必浪费唾沫,大概扫两眼也知道ext的输入控件是做什么的。
? 像TextField,TextArea,NumberField这类当然是可以随便输入的了。
? ComboBox,DateField继承自TriggerField。他们长相差不多,都是一个输入框右边带一个图片,点击以后就跳出一大堆东西来让你选择,输入框里头显示的是你选中的东西。
? Checkbox和Radio,ext没有过多封装,基本上还是原来的方式。
? Button,这个东东其实就是一个好看的div,跟comboBox一样,不是对原有组件的美化,而是重新做的轮子。你可以选择用以前那种难看的type="button",还是用咱们漂亮的div,看你的爱好了。type="submit"和type="reset"也一样没有对应的组件,都使用Button好了。
? 文件上传框,type="file",因为浏览器的安全策略,想上传文件,必须使用type="file",而且我们不能使用js修改上传框的值,所以非常郁闷,目前的方式是把它隐藏起来,然后在点击咱们漂亮的Button时,触发上传框的点击事件,从而达到上传的目的。在这方面extjs.com论坛上有不少实现上传的扩展控件,咱们可以参考一下。
4.4. 起点高撒,从comboBox往上蹦。
我觉得像TextField啊,TextArea啊,都是在原来的东西上随便加了几笔css弄出来的,大家都会用,所以没什么大搞头,最后综合起来一说就ok了。而这个comboBox跟原有的select一点儿关系都没有,完全是用div重写画的。所以,嘿嘿~
耳听为虚,眼见为实,先看看所谓的comboBox究竟是个什么模样。

漂亮不?漂亮不?怎么看都比原生select强哟。啦啦啦,咱们看看代码撒。不过呢,comboBox支持两种构造方式,一一看来。
4.4.1. 凭空变出个comboBox来。
>
4.4.2. 把select变成comboBox。
4.4.3. 破例研究下comboBox的内在本质哟
4.4.4. 嘿嘿~本地的做完了,试试远程滴。
4.4.5. 给咱们的comboBox安上零配件
4.4.6. 每次你选择什么,我都知道
4.4.7. 露一小手,组合上面所知,省市县三级级联。哈哈~
这是一个相当典型的案例,以前经常用select做,现在用comboBox实现的效果又会如何呢?
4.4.7.1. 先做一个模拟的,所有数据都在本地
4.4.7.2. 再做一个有后台的,需要放在服务器上咯
4.5. 还要做,字段验证呀,表单提交啊,表单布局咯,文件上传哟

第5章 雀跃吧!超脱了一切的弹出窗口
5.1. 呵呵~跳出来和缩回去总给人惊艳的感觉。
浏览器原声的alert(),confirm(),prompt()显得如此寒酸,而且还不能灵活配置,比如啥时候想加个按钮,删个按钮,或者改改按下按钮触发的事件了,都是难上加难的事情。
既然如此,为何不同ext提供的对话框呢?那么漂亮,那么好配置,可以拖啊,可以随便放什么东西,在里边用啥控件都可以,甚至放几个tab乱切换呀,连最小化窗口的功能都提供了。哈哈,神奇啊,完全可以让alert退役了。
5.2. 先看看最基本的三个例子
嘿嘿,为了加深认识,还是先去看看examples下的例子吧。1.x在dialog目录下。2.0在message-box目录下。
5.2.1. Ext.MessageBox.alert()
Ext.MessageBox.alert('标题', '内容', function(btn) {
    alert('你刚刚点击了 ' + btn);
});
           

现在可以通过第一个参数修改窗口的标题,第二个参数决定窗口的的内容,第三个参数是你关闭按钮之后(无论是点ok按钮还是右上角那个负责关闭的小叉叉),就会执行的函数,嘿嘿,传说中的回调函数。
5.2.2. Ext.MessageBox.confirm()
Ext.MessageBox.confirm('选择框', '你到底是选择yes还是no?', function(btn) {
    alert('你刚刚点击了 ' + btn);
});
           

选择yes或者是no,然后回调函数里可以知道你到底是选择了哪个东东。
5.2.3. Ext.MessageBox.prompt()
Ext.MessageBox.prompt('输入框', '随便输入一些东西', function(btn, text) {
    alert('你刚刚点击了 ' + btn + ',刚刚输入了 ' + text);
});
           

随便输入几个字,然后点按钮,它会告诉你输入了些什么东西
5.3. 如果你想的话,可以控制得更多
5.3.1. 可以输入多行的输入框
Ext.MessageBox.show({
    title: '多行输入框',
    msg: '你可以输入好几行',
    width:300,
    buttons: Ext.MessageBox.OKCANCEL,
    multiline: true,
    fn: function(btn, text) {
        alert('你刚刚点击了 ' + btn + ',刚刚输入了 ' + text);
    }
});
           

其实只需要show,我们就可以构造各种各样的窗口了,title代表标题,msg代表输出的内容,buttons是显示按钮,multiline告诉我们可以输入好几行,最后用fn这个回调函数接受我们想要得到的结果。
5.3.2. 再看一个例子呗
可能让我们对show这个方法的理解更深
Ext.MessageBox.show({
    title:'随便按个按钮',
    msg: '从三个按钮里随便选择一个',
    buttons: Ext.MessageBox.YESNOCANCEL,
    fn: function(btn) {
        alert('你刚刚点击了 ' + btn);
    }
});
           

我相信buttons这个参数是一个数组,里边的这个参数绝对了应该显示哪些按钮,Ext.MessageBox给我们提供了一些预先定义好的组合,比如YESNOCANCEL,OKCANCEL,可以直接使用。
5.3.3. 下一个例子是进度条
实际上只需要将progress这个属性设置为true,对话框里就会显示个条条。
Ext.MessageBox.show({
    title: '请等待',
    msg: '读取数据中',
    width:240,
    progress:true,
    closable:false
});
           

看到进度条了吧,不过它可不会自动滚啊滚的,你需要调用Ext.MessageBox.updateProgress让进度条发生变化。
另外多说一句,closable: false会隐藏对话框右上角的小叉叉,这样咱们就不能随便关掉它了。
现在让咱们加上更新进度条的函数,使用timeout定时更新,这样咱们就可以看到效果了。呵呵~效果真不错,这样咱们以后就可以使用进度条了。
var f = function(v){
    return function(){
        if(v == 11){
            Ext.MessageBox.hide();
        }else{
            Ext.MessageBox.updateProgress(v/10, '正在读取第 ' + v + ' 个,一共10个。');
        }
   };
};
for(var i = 1; i < 12; i++){
   setTimeout(f(i), i*1000);
}
           
5.3.4. 动画效果,跳出来,缩回去
超炫效果,让对话框好像是从一个按钮跳出来的,关闭的时候还会自己缩回去。你可以看到它从小变大,又从大变小,最后不见了。实际上的配置缺非常简单,加一个animEl吧。让我们看看上边那个三个按钮的例子会变成什么样子。
Ext.MessageBox.show({
    title:'随便按个按钮',
    msg: '从三个按钮里随便选择一个',
    buttons: Ext.MessageBox.YESNOCANCEL,
    fn: function(btn) {
        alert('你刚刚点击了 ' + btn);
    },
    animEl: 'dialog'
});
           
animEl的值是一个字符串,它对应着html里一个元素的id,比如<div id="dialog"></div>。指定好了这个,咱们的对话框才知道根据哪个元素播放展开和关闭的动画呀。
只需要这样,咱们就得到动画效果,嘿嘿,截不到动画效果的图,大家自己去看吧。
以上的例子在examples里都可以找到,不过咱们也提供了一份自己的例子,1.x在lingo-sample/1.1.1/05-01.html。2.0在lingo-sample/2.0/05-01.html。
好消息是,这部分的api没有什么改动。不过表现形式上有些差别,如果像我在例子里写的那样,一次生成N个MessageBox,只能显示最后一个对话框。
不过在1.x里明显有一些数据同步的问题,1.x里的updateProgress甚至可以影响其他对话框的msg,以及可以关闭最后那个对话框。2.0里至少是好的。
5.4. 让弹出窗口,显示我们想要的东东,比如表格
2.0需要window来完成这个任务,1.x版的BasicDialog稍后加上。
5.4.1. 2.0的弹出表格哦
稍微说一下window咋用呢?其实看起来跟MessageBox差不多啦,只是可以在里边随便放东西,现在先看个单纯的例子。
var win = new Ext.Window({
    el:'window-win',
    layout:'fit',
    width:500,
    height:300,
    closeAction:'hide',

    items: [{}],

    buttons: [{
        text:'按钮'
    }]
});
win.show();
           
首先要讲明的是,这个window需要一个对应的div呀,就像el对应的'window-win'一样,这个div的id就应该等于'window-win',然后设置宽和高,这些都很明朗。
其次,需要设置的是布局类型,layout:'fit'说明布局会适应整个window的大小,估计改变大小的时候也要做响应的改变。
closeAction:'hide'是一个预设值,简单来说就是你用鼠标点了右上角的叉叉,会执行什么操作,这里就是隐藏啦。问为啥是隐藏?因为,因为预设啦,乖,背下来撒。
items部分,嘿嘿~就是告诉咱们的window里要有什么内容啦。这里放表格,放树形,吼吼。
buttons里设置在底端显示的按钮。我们就为了试一下,弄了一个按钮,但是按了没反应,嘿嘿。
最后调用一下show(),让窗口显示出来。
看一下截图啦,更直观。

中间的空白就是items:[{}]的杰作,默认{}会成为一个Ext.Panel,咱们什么都没定义,里边自然什么都没有。当然500*300不会只有这么大,但是为了让图片小一点儿,我把它拖下了,嘿嘿~自动支持的修改大小效果,帅吧?
例子超简单,见lingo-sample/2.0/05-02.html。
5.4.2. 向2.0的window里加表格
唉,地方都划出来了,弄个表格放进去就好了呗。
首先弄一个grid,超简单那种。我是直接把第二章的例子给copy了过来,嘿嘿,表格还是那个表格哟。
有了表格,直接扔到window里,然后ok,哈哈~效果如下:

看到没?表格出来了,如果想加分页条什么的,只管动手,别怕伤到窗口。
现在回头让我们看看,需要注意些什么。
? 第一,grid不用调用render()了,只要加入了window,在win.show()的时候,会自动渲染里边的组件。
? 第二,html里,要把grid对应的div写到window的div里面,嵌套关系。
? <div id="window-win">
?     <div id="grid"></div>
? </div>
                   
? 第三,如果还不知道怎么把grid放进window里,我给你看下代码。
? var win = new Ext.Window({
?     el:'window-win',
?     layout:'fit',
?     width:500,
?     height:300,
?     closeAction:'hide',
?
?     items: [grid],
?
?     buttons: [{
?         text:'按钮'
?     }]
? });
                   
看到items:[grid]了吗?就这么简单哟。
好了,具体例子在lingo-sample/2.0/05-03.html。敬请大家继续关注。
5.5. 更进一步撒。
希望上边的那些足够了,如果不够,咱们还有更厉害的,如果你看了examples里的例子,就知道里边还有更复杂的对话框,你甚至可以在里边用各种布局。
第 6 章 奔腾吧!让不同的浏览器里显示一样的布局。
6.1. 有了它,我们就可以摆脱那些自称ui设计师的人了。
对布局很是不熟,至今为止,也是一直在抄土豆demo里的BorderLayout,frank的deepcms ProjectTracker里的ViewPort布局而已,不过有了布局,咱们不用再去摆弄frameset了,只需要div就可以做成端端正正的布局,嗯,只这一点儿就吸引了多少眼球啊。
唉,咱们一起学学关于布局的用法吧。
6.2. 关于BORDERLAYOUT

理论上说,把整个窗口切成五块就够了吧?东南西北中,east,south,west,north,center其中只有center中间这个部分是必须的,你完全可以把围绕在它四周的东西当作配角。
这样说还是太抽象,这个时候效果图绝对比其他途径来的直观。

实际上代码还是比较干净的。
var mainLayout = new Ext.BorderLayout(document.body, {
    north: {
        initialSize: 50
    }, south: {
        initialSize: 50
    }, east: {
        initialSize: 100
    }, west: {
        initialSize: 100
    }, center: {
    }
});

mainLayout.beginUpdate();
mainLayout.add('north', new Ext.ContentPanel('north-div', {
    fitToFrame: true, closable: false
}));
mainLayout.add('south', new Ext.ContentPanel('south-div', {
    fitToFrame: true, closable: false
}));
mainLayout.add('east', new Ext.ContentPanel('east-div', {
    fitToFrame: true, closable: false
}));
mainLayout.add('west', new Ext.ContentPanel('west-div', {
    fitToFrame: true, closable: false
}));
mainLayout.add('center', new Ext.ContentPanel('center-div', {
    fitToFrame: true
}));
mainLayout.endUpdate();
html需要五个div与其对应,div与ContentPanel是一一对应的,请看他们的id。
<div id="north-div">north</div>
<div id="south-div">south</div>
<div id="east-div">east</div>
<div id="west-div">west</div>
<div id="center-div">center</div>
这个其实挺有意思的,你必须先构造一个BorderLayout,指定需要渲染的部分,这里是document.body,并指定5个部分的初始化大小,然后调用beginUpdate()让整个布局先不要刷新,当然我们最后会调用endUpdate()刷新布局,这样用户就获得了更好的体验。
beginUpdate()之后,我们立刻使用add方法,向5个部分分别加入Ext.ContentPanel,这些面板的第一个参数是对应dom的id,后边是附加的参数,比如fitToFrame:true,它告诉面板在布局区域改变大小的时候调整自己的大小,然后是closable:false,这样用户就不能点击关闭按钮,关闭这个面板。
好了,你也看到了,这五个部分明显已经分隔开了,使用的时候我们只需要在合适的地方放上合适的东西就行了。
例子在lingo-sample/1.1.1/06-01.html。
6.3. 嗯,不如再看看附加效果

其实,即使只能在不同浏览器,把一个窗口切成相同的部分,也是足够了,不过ext带给我们的不仅仅是如此,让我们再看看其他部分吧。
6.3.1. 先看看split

var mainLayout = new Ext.BorderLayout(document.body, {
    north: {
        initialSize: 50,
        split: true
    }, south: {
        initialSize: 50,
        split: true
    }, east: {
        initialSize: 100,
        split: true
    }, west: {
        initialSize: 100,
        split: true
    }, center: {
    }
});
让我们给所有区域都加上split:true,看看会有什么效果?

请注意一点,这并不仅仅是那些边框变粗了,split让我们可以自由拖动边框,让用户可以改变各个区域的大小。
当然,我们不会让用户为所欲为的,让我们加上一点点限制,这点儿限制绝对不会让用户感到难堪。
var mainLayout = new Ext.BorderLayout(document.body, {
    north: {
  相关解决方案