Ext2.0 的 form 不单增加了时间输入控件、隐藏输入控件,还修改了创建方法,通过 formpanel 代替了原来 form , column 也根据新的布局定义更新了定义方式。总体来说,定义一个 form 更简单便捷了。本文将通过一个实例介绍一下 2.0 的 form 的创建以及其大部分控件的使用方法,因水平有限,错漏难免,忘大家多多谅解!
我们先来看看我们将要设计的 form 的情况:
呵呵, form 有点杂乱,不过在这个 fomr 里包含了绝大部分 Ext2.0 的控件,我将会和大家一起探讨一下这些控件的使用。
在创建一个 form 之前,我们先增加以下语句:
Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = 'side'; |
第一句的目的是为需要的元件提供提示信息功能, form 的主要提示信息就是客户端验证的错误信息了。
第二句的目的就是设置控件的错误信息显示位置,主要可选的位置有:
位置值 |
描述 |
qtip
|
当鼠标移动到控件上面时显示提示 |
title |
在浏览器的标题显示,但是测试结果是和 qtip 一样的 |
under |
在控件的底下显示错误提示 |
side |
在控件右边显示一个错误图标,鼠标指向图标时显示错误提示 |
[element id]
|
错误提示显示在指定 id 的 HTML 元件中 |
这个大家可以根据各人喜好设置,我习惯使用“ side ”,这里有一点要注意的,就是注意控制控件的宽度,以防不够宽度显示错误图标,这个下面会说到。
好了,现在创建我们的 form , 2.0 的方法就是直接创建一个 formpanel :
var simpleForm = new Ext.FormPanel({ labelAlign: 'left', title: ' 表单例子 ', buttonAlign:'right', bodyStyle:'padding:5px', width: 600, frame:true, labelWidth:80, items: [], buttons: [] }); simpleForm.render(document.body); |
在 formpanle 里,我们定义了 form 控件的标题是在左边的( labelAlign: 'left' ); form 的标题栏显示标题“表单的例子”;它的按钮位置是在右对齐的( buttonAlign:'right' );边的类型设置了内补丁 5px ( bodyStyle:'padding:5px' );总宽度是 600px ;设置了面板的边角是圆弧过度的( frame:true ),我设置这个属性主要目的不是因为边角,而是因为背景,如果不设置这个,背景颜色将为白色,设置了这个将会加入海蓝色的背景图,好看点;还设置了 form 控件的标题宽度是 80px ( labelWidth:80 )。还有一些其它的设置选项,我这里就不多说,大家可以参看 2.0 的 API 。
items 数组的设置是我们的重点了, form 上的所有控件都是在这里设置的。
从 form 的结构图中看到, form 整体上是分了两列的(实际上不是的,呵呵)。因为要分列,所以要使用 columnLayout 类。在使用 columnLayout 类之前,我们需要了解一下 CSS 中 float 属性的作用,改属性主要作用是设置对象是否及如何浮动,属性值为 none 、 left 和 right 三个。 column 设置是 left ,意思就是对象浮在左边的。那这个有什么作用呢?其实这个和我们在 word 中输入文字,默认文字是左对齐的,当一行文字的宽度超过页面的宽度时将自动换行是一样的。 我们通过一个例子来说明一下。
首先我们定义一个 div ,背景色是黑色,宽度和高度都是 200 :
<div style='background:black;width:200px;height:200px;'> </div> |
然后在里面加入 2 个 div ,每个宽度和高度都是 200 ,背景色一个是红色,一个是绿色:
<div style='background:black;width:200px;height:200px;'> <div style='background:red;width:50px;height:50px;'></div> <div style='background:green;width:50px;height:50px;'></div> </div> |
我们来看看效果:
在没有使用 float 之前,两个子 div 是分别各占一行的。好,现在我们在两个子 div 中加入“ float:left ”在看看效果:
<div style='background:black;width:200px;height:200px;'> <div style='background:red;width:50px;height:50px;float:left;'></div> <div style='background:green;width:50px;height:50px;float:left;'></div> </div> |
两个子 div 出现在同一行了。我们复制一下两个子 div ,粘贴两次,然后看看效果:
<div style='background:black;width:200px;height:200px;'> <div style='background:red;width:50px;height:50px;float:left;'></div> <div style='background:green;width:50px;height:50px;float:left;'></div> <div style='background:red;width:50px;height:50px;float:left;'></div> <div style='background:green;width:50px;height:50px;float:left;'></div> <div style='background:red;width:50px;height:50px;float:left;'></div> <div style='background:green;width:50px;height:50px;float:left;'></div> </div> |
6 个子 div 有序的按左对齐方式排列在一起了,当一行的子 div 的宽度超过了父 div 的宽度时,子 div 自动换行到了第二行。
不知道大家是否看得明白?看不明白自己再动手改变一下子 div 的宽度和高度,看看效果。 column 的工作方式就是这样的。明白这个很重要,因为在定义 checkbox 和 radio 的时候,如果想它们的选项在同一行,就要注意 column 的宽度,不然就无法让他们在同一行。不过现在 column 是通过百分比来定义宽度的,我们只要控制好百分比就行了。
好了,我们继续写 form ,因为要用到 column ,所以我们先在 formpanel 的 itmes 加入一个 column 的定义:
{ layout:'column', border:false, labelSeparator:' : ', items:[] } |
代码里定义了在这里使用的是 columnlayout ( layout:'column' );没有边( border:false );标题的分隔符号我们用中文冒号代替英文的冒号( labelSeparator:' : ' )。 coulmnLayout 里的控件将定义在 items 里。
我们首先在 items 里加入一个常用输入控件,是用来输入姓名的:
{ columnWidth:.5, layout: 'form', border:false, items: [{ xtype:'textfield', fieldLabel: ' 姓名 ', name: 'name', anchor:'90%' }] } |
我们设置了该列的宽度占总宽度的 50% ( columnWidth:.5 );在布局里放了一个 formlayout 用来放置控件( layout: 'form' ); formlayout 也是没有边的( border:false )。在 formlayout 里有一个类型为 textfield' ( xtype:'textfield' )的输入控件。控件标题为姓名( fieldLabel: ' 姓名 ' ),输入框( input )的 name 属性设置“ name ”( name: 'name' ),输入框的长度为列宽减去标题的宽度后的 90% ( anchor:'90%' ),余下的 10% 的是给显示错误信息图标用的。
在加入性别的 radio 控件时就要注意了,这里需要加入两个 radio ,第一 radio 是有标题的,第二是没有的,而且两个 radio 加起来的宽度应该正好是余下的列宽( 50% ):
{ columnWidth:.25, layout: 'form', border:false, items: [{ style:'margin-top:5px', xtype:'radio', fieldLabel: ' 性别 ', boxLabel:' 男 ', name: 'sex', checked:true, inputValue:' 男 ', anchor:'95%' }] },{ columnWidth:.25, layout: 'form', labelWidth:0, labelSeparator:'', hideLabels:true, border:false, items: [{ style:'margin-top:5px', xtype:'radio', fieldLabel: '', boxLabel:' 女 ', name: 'sex', inputValue:' 女 ', anchor:'95%' }] } |
从代码中可以看到,除了列宽设置为 25% 外,其它的列设置和第一控件是一样。 Formlayout 里加入了一个类型为 radio 的控件。控件的标题是性别,控件的选择显示文本是男( boxLabel:' 男), input 的 name 属性值是 sex ( name: 'sex' ),该控件默认是已选的( checked:true ),控件的值( value )是男( inputValue:' 男 ' ), input 的宽度是 95% 。在这里我还设置一个 css 属性,顶部的外补丁为 5px ( style:'margin-top:5px' ),原因是为了选择按钮和标题对齐,大家可以将该属性去掉然后看看效果。
第二个 raido 控件的列设置就有所不同,因为它不需要标题,所以要设置隐藏标题( hideLabels:true ),标题的宽度设置为 0 ( labelWidth:0 ) , 还要设置其标题分隔符号为空( labelSeparator:'' )。其余的设置和第一个 radio 的设置没有不同,只是 input 的值不同了。
我们已经设置了 3 列, 3 列的列宽分别为 50% 、 25% 、 25% ,根据 float 的原则,下一列将从第二行开始。
在第二行第一列我们要增加的是一个日期选择控件:
{ columnWidth:.5, layout: 'form', border:false, items: [{ xtype:'datefield', fieldLabel: ' 出生日期 ', name: 'birthday', anchor:'90%' }] } |
日期控件的列宽也是 50% ,列的其它设置没有变化。控件的类型为 datefield ,标题是出生日期, input 的 name 属性是 birthday , intput 宽度也是设置了 90% ,出来留出空位给错误信息外,还可以让控件与上一行的姓名的宽度相同,整列看起来比较整齐。
日期控件的设置和普通文本输入的设置一样简单,这里就不多说了。不过要说到的是汉化的问题。在 2.0 版自带的本地化文件 ext-lang-zh.js 中存在一些小 bug ,我们需要自己修改一下。
首先要修改的是周的显示,原来的定义是:
Date.dayNames = [ " 周日 ", " 周一 ", " 周二 ", " 周三 ", " 周四 ", " 周五 ", " 周六 " ]; |
因为在日期选择中显示的区域不够宽,只能显示一个汉字,所以需要将上面定义的把“周”去掉,修改为:
Date.dayNames = [ " 日 ", " 一 ", " 二 ", " 三 ", " 四 ", " 五 ", " 六 " ]; |
在年份和月份选择中的按钮文字还是英文“ ok ”和“ cancel ”的,这里我们也需要修改一下:
if(Ext.DatePicker){ Ext.apply(Ext.DatePicker.prototype, { todayText : " 今天 ", minText : " 日期在最小日期之前 ", maxText : " 日期在最大日期之后 ", disabledDaysText : "", disabledDatesText : "", monthNames : Date.monthNames, dayNames : Date.dayNames, nextText : ' 下月 (Control+Right)', prevText : ' 上月 (Control+Left)', monthYearText : ' 选择一个月 (Control+Up/Down 来改变年 )', todayTip : "{0} (Spacebar)", okText : " 确定 ",
cancelText : " 取消 ",
format : "y 年 m 月 d 日 " }); } |
上面定义中黑色字体部分就是要加入的代码。如果不喜欢默认格式是“ y 年 m 月 d 日”,需要修改:
if(Ext.form.DateField){ Ext.apply(Ext.form.DateField.prototype, { disabledDaysText : " 禁用 ", disabledDatesText : " 禁用 ", minText : " 该输入项的日期必须在 {0} 之后 ", maxText : " 该输入项的日期必须在 {0} 之前 ", invalidText : "{0} 是无效的日期 - 必须符合格式: {1}", format : "Y-m-d"
}); } |
修改 DatePicker 不会改变 DateField 的格式的,这个自己根据情况决定,呵呵。
我们继续,现在需要加入一个学历的下拉选择控件。下来选择控件最重要的一个定义就是数据的定义的,数据定义错误,可能得不到我们需要的效果,也是很多朋友感觉最麻烦的地方。
{ columnWidth:.5, layout: 'form', border:false, items: [{ xtype:'combo', store: new Ext.data.SimpleStore( { fields: ["retrunValue", "displayText"], data: [[1,' 小学 '],[2,' 初中 '],[3,' 高中 '],[4,' 中专 '],[5,' 大专 '],[6,' 大学 ']] }), valueField :"retrunValue", displayField: "displayText", mode: 'local', forceSelection: true, blankText:' 请选择学历 ', emptyText:' 选择学历 ', hiddenName:'education', editable: false, triggerAction: 'all', allowBlank:false, fieldLabel: ' 学历 ', name: 'education', anchor:'90%' }] } |
列的定义就不说了,没变化。在 items 里,类型设置成 combo 了,在这里定义了一个 sotre 属性,就是选择值存储的地方,因为是在客户端的数据,所以使用了一个简单存储( SimpleStore )。在存储里,我们通过一个数组定义了 retrunValue 和 displayText 两个字段。 retrunValue 字段指定是提交给后台的值, displayText 字段指定是在下拉中显示的选择值。然后我们在 data 里定义了几组数据( data: [[1,' 小学 '],[2,' 初中 '],[3,' 高中 '],[4,' 中专 '],[5,' 大专 '],[3,' 大学 ']] ),我们可以看到,每组数据都是根据 fiedls 的定义来组成的,数组里第一个值就是 retrunValue 的值,第二个值就是 displayText 的值,例如 [1,' 小学 '] ,就表示 retrunValue 是 1 , displayText 是小学。
下面就是很重要的一步了,设置下拉选择框的值和显示文本。本例中设置了下拉选择框的提交值对象的是存储中的 retrunValue 字段( valueField :"retrunValue" ),显示文本是存储中的 displayText 字段( displayField: "displayText" ),通过这两个设置就可将存储中的数据和下拉框对应起来。
因为数据是在本地,所以设置了模式为 local ( mode: 'local' )。该下拉列表只允许选择,不允许输入( editable: false ),而且是必须选择一个选项( forceSelection: true )。在没有选择值时显示为选择学历( emptyText:' 选择学历 ' )。提交 form 时,该项如果没有选择,则提示错误信息“请选择学历”( blankText:' 请选择学历 ' )。该选项值不允许为空( allowBlank:false )。 大家要注意的是 hiddenName 和 name 属性, name 只是改下拉的名称,作用是可通过,而 hiddenName 才是提交到后台的 input 的 name 。如果没有设置 hiddenName ,在后台是接收不到结构的,这个大家一定要注意。
因为这个下拉是只能选择的,所以一定要设置属性 triggerAction 为 all ,不然当你选择了某个选项后,你的下拉将只会出现匹配选项值文本的选择项,其它选择项是不会再显示了,这样你就不能更改其它选项了。
如果要为控件设置默认值,就设置属性 value , value 的值要设置为提交给后台的值,不要设置为显示文本。例如本例要设置大学为默认值得,则设置 value 的值为 6 。
现在到第三行了,我们要创建一个 checkbox 选项输入:
{ columnWidth:.35, layout: 'form', border:false, items: [{ xtype:'checkbox', fieldLabel: ' 权限组 ', boxLabel:' 系统管理员 ', name: 'popedom', inputValue:'1', anchor:'95%' }] },{ columnWidth:.2, layout: 'form', labelWidth:0, labelSeparator:'', hideLabels:true, border:false, items: [{ xtype:'checkbox', fieldLabel: '', boxLabel:' 管理员 ', name: 'pepedom', inputValue:'2', anchor:'95%' }] },{ columnWidth:.2, layout: 'form', labelWidth:0, labelSeparator:'', hideLabels:true, border:false, items: [{ xtype:'checkbox', fieldLabel: '', boxLabel:' 普通用户 ', name: 'pepedom', inputValue:'3', anchor:'95%' }] },{ columnWidth:.25, layout: 'form', labelWidth:0, labelSeparator:'', hideLabels:true, border:false, items: [{ xtype:'checkbox', fieldLabel: '', boxLabel:' 访客 ', name: 'pepedom', inputValue:'4', anchor:'95%' }] } |
checkbox 的设置和 radio 的设置大同小异,大家注意列宽的定义就行。
第四行的两个输入框主要是测试通过 vtypes 属性来验证输入框的输入的:
{ columnWidth:.5, layout: 'form', border:false, items: [{ xtype:'textfield', fieldLabel: ' 电子邮件 ', name: 'email', vtype:'email', allowBlank:false, anchor:'90%' }] },{ columnWidth:.5, layout: 'form', border:false, items: [{ xtype:'textfield', fieldLabel: ' 个人主页 ', name: 'url', vtype:'url', anchor:'90%' }] }] } |
这里的定义和普通的文本输入框没什么区别,只是多了一个 vtypes 的属性定义。 Vtypes 里总共定义了 email 、 url 、 alpha 和 alphanum 四种类型数据格式, email 和 url 这个不用介绍了,呵呵。 alpha 是字母和下划线的组合, alphanum 是字母、下划线和数字的组合。
下面要加入一个 tabpanel ,加入 3 个 tab 页。
{ xtype:'tabpanel', plain:true, activeTab: 0, height:235, defaults:{bodyStyle:'padding:10px'}, items:[] } |
要注意的是,这个 tabpanel 不是在上面 coulmn 的 items 里加的,因为不在 column 里。我们加在 formpanel 里。把 item 类型设置为 'tabpanel' 就行了,然后将标签页头的背景设置为透明的( plain:true ),当前活动的标签页是第一页( activeTab: 0 ),高度设置为 235px ( height:235 ), tab 页的面板使用内补丁 10px ( defaults:{bodyStyle:'padding:10px'} )。
好了,现在在 tabpanel 的 items 加入标签页。第一页主要有两个输入控件,一个是 vtypes 类型 alphanum 的登录输入框和一个密码输入框。
{ title:' 登录信息 ', layout:'form', defaults: {width: 230}, defaultType: 'textfield',
items: [{ fieldLabel: ' 登录名 ', name: 'loginID', allowBlank:false, vtype:'alphanum', allowBlank:false },{ inputType:'password', fieldLabel: ' 密码 ', name: 'password', allowBlank:false }] } |
在标签定义了,设置了标签标题是登录信息( title:' 登录信息 ' ),控件容器是 formlayout ( layout:'form' ),控件的默认宽度是 230px ( defaults: {width: 230} ),默认控件类型是 textfield ( defaultType: 'textfield' )。
两个控件的定义与前面的 textfield 定义没什么区别,只是密码输入框需要定义 input 控件的类型为 password ( inputType:'password' )。两个控件都不允许为空( allowBlank:false )。
第二个标签页里有 numberfield 、 timefield 和 textfield 三个控件:
{ title:' 数字时间字母 ', layout:'form', defaults: {width: 230}, defaultType: 'textfield',
items: [{ xtype:'numberfield', fieldLabel: ' 数字 ', name: 'number' },{ xtype:'timefield', fieldLabel: ' 时间 ', name: 'time' },{ fieldLabel: ' 纯字母 ', name: 'char', vtype:'alpha' }] } |
Numberfield 顾名思义就是只能输入数字的输入控件。在该例子,没做最大值、最小值任何限制,如果要设置最大值和最小值,分别设置 maxValue 和 minValue 两个属性就行了。如果要设置数字输入长度,例如身份证号码,可以设置 maxLength 和 minLength 两个属性。可以通过设置 maxText 、 minText 、 maxLengthText 和 minLengthText 设置各自的验证出错信息。可通过 allowDecimals 属性设置是否只允许输入整型值,默认值是 true ,允许输入浮点数。设置 allowNegative 设置是否只允许输入正数,默认值是 true ,允许输入正负数。通过 decimalPrecision 属性可设置小数点后的位数,默认值是 2 位。
timefield 是新增加的时间输入控件,起弥补日期输入控件不能输入时间的作用。它的定义也很简单,设置类型为 timefield 就行了。 timefield 默认时间格式是 12 小时制的,我们可通过修改 format 属性来修改其数据格式。通过设置 increment 属性可设置下拉选择值得时间区间,默认值是 15 分钟的。还可以和数字输入控件一样设置最大值和最小值。下拉的设置和 combobox 是一样的。
在目前的版本中, timefield 类还没有汉化,所以我们要在本地文件中加入 timefield 的汉化定义:
if(Ext.form.TimeField){ Ext.apply(Ext.form.TimeField.prototype, { format:'G:i:s', minText : " 该输入项的时间必须大于或等于: {0}", maxText : " 该输入项的时间必须小于或等于: {0}", invalidText : "{0} 不是有效的时间 ", }); } |
在这里,我默认定义了时间格式是 24 小时制的,小时为个位数是不加前缀 0 。
最后一个加入的是测试纯字母输入的,和 email 等是一样的,我就不介绍了。
在最后一个 tab 页中加入了一个 textarea 输入:
{ title:' 文本区域 ', layout:'fit', items: { xtype:'textarea', id:'area', fieldLabel:'' } } |
和 textfield 一样,只要设置类型为 textarea 就可以了,唯一的区别是为了让 textarea 和面板自适应面板,使用了 fitlayout 作为它的容器,所以在这里我们不用设置 textarea 的宽度和高度。
最后一步就是为 form 添加按钮了,在 formpanel 的 buttons 属性中我们加入了一个保存按钮和取消按钮:
buttons: [{ text: ' 保存 ', handler:function(){ if(simpleForm.form.isValid()){ this.disabled=true; simpleForm.form.doAction('submit',{ url:'test.asp', method:'post', params:'', success:function(form,action){ Ext.Msg.alert(' 操作 ',action.result.data); this.disabled=false; }, failure:function(){ Ext.Msg.alert(' 操作 ',' 保存失败! '); this.disabled=false; } }); } } },{ text: ' 取消 ', handler:function(){simpleForm.form.reset();} }] |
在 formpanel 类中, form 属性指向的是 formpanle 里的 basicform 对象,我们可通过 formpanle.form 来使用该 basicform 对象。在被例子,我们已经将 formpanel 对象赋值给了 simpleForm 这个变量,所以我们可以通过 simpleForm.form 访问面板里的 basicform 对象。
在 buttons 里定义的按钮默认是 Ext.Button ,所以按钮的属性定义可以查看 Ext.Button 的 API 。在这里两个按钮都没用到其它属性,只是设置了显示文本( text )和单击事件。
保存按钮要做的就是先做 basicform 的客户端验证( simpleForm.form.isValid() ),验证通过了则设置该按钮状态为 disable ,防止 2 次提交。然后调用 simpleForm.form.doAction 方法提交数据。 doAction 方法的第一个参数“ submit ”的意思是表示执行的是提交操作,提交的后台页面是 test.asp ( url:'test.asp' ),提交方式是 post ( method:'post' ),没有其它提交参数( params:'' ),提交成功后执行 success 定义的函数,本例只是显示一个保存成功信息。 后台返回的数据格式是需要我们注意的,一定要 json 格式,而且必须包含“ success:true ”,不然不会执行 success 定义的函数。 success 定义的函数返回两个参数,第一是 form 本身,第二个是 ajax 返回的响应结果,在 action.result 这个 json 数组了保存了后台返回的数据。例如本例后台返回的 json 结构是“ {success:true,data:~~~} ”,其中 data 部分我将提交的数据将字段名和数据组合成一个字符串。在 success 函数中,我通过“ Ext.Msg.alert(' 操作 ',action.result.data); ”将 data 数据显示出来。我们还定义 failure 函数,就是网络通讯存在问题的时候将显示错误信息。
取消按钮就是简单的 reset 一下 form 的控件。
如果想 form 按以前的老办法提交,可以在 formpanel 的定义中加入一下设置:
} |
第一个设置的目的是取消 formpanel 的默认提交函数。第二就是设置新的提交方式为旧方式提交。
至此,我们已经简单的学习一次 2.0 版中的 form 控件,希望大家能从中获得收益。如果有什么疑问和建议,请联系我。多谢!
本例子的代码请单击 这里 下载,例子在 form 目录下。
本例子的完整代码:
|
后台文件的代码 (ASP) :
|
CSS 属性 float 的测试文件代码:
|