表单是HTML中的一个元素,用来和服务器交互数据,将用户数据提交到服务器中,由服务器的CGI程序进行处理,例如保存到数据库等操作。原生的HTML表单中可以包含文本框、文本域、下拉框、复选框、单选框等元素,但日历控件、下拉分页控件等都不是默认HTML表单元素,需要JavaScript编写的AJAX框架才能支持,我们可以使用很多开源的日历控件、下拉分页控件,但使用开源的控件会导致WEB界面的代码不统一,维护不方便的缺点。而EXT JS框架基本上实现了我们开发中需要的所有控件,所以,本文主要介绍由EXT JS4的表单和控件的开发方法。
EXT JS适合开发企业级系统或者是后台管理系统,不合适开发网站,主是要界面风格的问题,另外是由于体积比较大,下载速度比较慢,不过以现在的网速来讲,这些问题都是可以忽略。笔者很喜欢EXT JS面像对象的编程风格,可读性强、扩展性好。其实EXT JS自身的控件封装的已经非常优秀了,但我还是想不通为什么总是有很多人喜欢把业务代码再做一次或者很多次封装(过度封装),这样的代码其实是很糟糕的,因为后续的开发人员需要花很多的精力去阅读代码,查找和调试都非常困难,所以,建议大家在写代码的时候尽量写的通俗易懂,特别是JavaScrip代码,不像JAVA一样可以通过链接方式查找。
本文介绍的表单包含了三种表单元素,分别是文本框、文本域和日历控件,如下图:
<strong><%@ page pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <%-- 样式文件,加载全部样式 --%> <link rel="stylesheet" type="text/css" href="<%=path%>/javascript/extjs-4.1.0/resources/css/ext-all.css" /> <%-- ext js 文件 --%> <script type="text/javascript" src="<%=path%>/javascript/extjs-4.1.0/ext-all.js"></script> <script type="text/javascript" src="<%=path%>/javascript/extjs-4.1.0/ext-lang-zh_CN.js"></script> </strong>
我们需要新建一个JS文件,用来编写表单代码,也可以在JSP中的<JavaScript>标签,为了使用代码更具可读性,我们选择新建一个JS文件,文件名为editCardType.js。
EXT JS4引入了面像对象的编程风格,允许程序员定义自定义表单类型,继承、重写父类的方法,下面我们先来定一个“编辑卡类型”的表单类型。
Ext.define('MCS.basicOperation.cardType.editCardType', {…….})
这个模型我们继承自window类,即弹出框。弹出框表单可以解决页面宽度有限的问题,通常我们会将Data Grid控件充满整个页面,然后通过按钮点击事件弹出表单。
extend: 'Ext.window.Window'
重写initComponent方法,在这里我们编写初始化表单组件的代码。
var me = this; Ext.applyIf(me, { items: [ { xtype: 'hidden', fieldLabel: 'id', labelAlign: 'right', id:'id' }, { xtype: 'textfield', fieldLabel: '卡类名称', labelAlign: 'right', id:'cardName' }, { xtype: 'textfield', fieldLabel: '卡类编号', labelAlign: 'right', id:'cardNum' }, { xtype: 'textfield', fieldLabel: '日消费最大金额', labelAlign: 'right', id:'maxConsumeSum' }, { xtype: 'textfield', fieldLabel: '日消费最大次数', labelAlign: 'right', id:'maxConsumeTimes' }, { xtype: 'textfield', width: 255, fieldLabel: '折扣', labelAlign: 'right', id:'discount' }, { xtype: 'textfield', fieldLabel: '卡类型', labelAlign: 'right', id:'type' }, { xtype: 'datefield', fieldLabel: '有效日期', labelAlign: 'right', id:'validTime' }, { xtype: 'textfield', fieldLabel: '有效天数', labelAlign: 'right', id:'validDays' }, { xtype: 'textareafield', width: 509, fieldLabel: '备注', labelAlign: 'right', id:'remark' } ], dockedItems: [ { xtype: 'toolbar', dock: 'bottom', layout: { pack: 'center', type: 'hbox' }, items: [ { xtype: 'button', width: 56, text: '保存', listeners: { click: { fn: me.onButtonClick1, scope: me } } }, { xtype: 'button', width: 60, text: '关闭', listeners: { click: { fn: me.onButtonClick, scope: me } } } ] } ] }); me.callParent(arguments);
最后一步我们需要将提交表单内容到服务器,服务端的代码大家可以采用任意一种语言编写,本文我们用JAVA的Struts2框架编写。首先定义一个按钮单击事件的回调方法:
onButtonClick1: function(button, e, eOpts) {……}
使用Ext.getCmp()方法获取表单元素值,Ext.getCmp()是一个神奇的方法,可以获取任意的Ext元素对象,需要注意的是,采用Ext.getCmp()是根据ID查找元素的,所以获取的元素必须要有一个ID,当我们用Ext.getCmp()获取到相应的表单元素对象后,可以调用getValue()取得该元素的值,示例如下:
var cardNum=Ext.getCmp("cardNum").getValue();
将获取到的表单元素组装成JSON格式的数据,以便作为AJAX请求的参数提交到服务器
var json='{"oid":"'+id+'","cardName":"'+cardName+'","cardNum":"'+cardNum+'","maxConsumeSum":"'+maxConsumeSum+'","maxConsumeTimes":"'+maxConsumeTimes+'","validDays":"'+validDays+'","validTime":"'+validTime+'","type":"'+type+'","discount":"'+discount+'","remark":"'+remark+'"}';
编写AJAX请求,将数据提交到服务端。EXTJS提交表单有两种方法,一种是采用表单提交,一种是采用AJAX提交,本文采用的是AJAX提交方式。如果你是文件上传的表单的话,则必须要采用表单提交的方式提交,否则服务端接收不到数据。
Ext.Ajax.request({ url: 'saveCardType', headers: { 'userHeader': 'userMsg' }, method: 'POST', params:{data:json}, success: function (response, options) { // Ext.MessageBox.alert('成功', '从服务端获取结果: ' + response.responseText); var grid = Ext.create('MCS.basicOperation.cardType.cardTypeListGridPanel'); grid.getStore().load(); Ext.create('Ext.container.Viewport', { layout : 'fit', renderTo : Ext.getBody(), items : [ grid ] }); }, failure: function (response, options) { Ext.MessageBox.alert('失败', '请求超时或网络故障,错误编号:' + response.status); var grid = Ext.create('MCS.basicOperation.cardType.cardTypeListGridPanel'); grid.getStore().load(); Ext.create('Ext.container.Viewport', { layout : 'fit', renderTo : Ext.getBody(), items : [ grid ] }); } });
在保存按钮中,编写按钮触发事件,通常我们都是用按钮的单击事件。(难道你想让用户点两下鼠标才能提交表单吗?)
listeners: { click: { fn: me.onButtonClick1, scope: me } }
完整的代码如下:
onButtonClick1: function(button, e, eOpts) { var win = Ext.getCmp('editCardType'); var id=Ext.getCmp("id").getValue(); var cardName=Ext.getCmp("cardName").getValue(); var cardNum=Ext.getCmp("cardNum").getValue(); var maxConsumeSum=Ext.getCmp("maxConsumeSum").getValue(); var maxConsumeTimes=Ext.getCmp("maxConsumeTimes").getValue(); var validDays=Ext.getCmp("validDays").getValue(); var validTime=Ext.getCmp("validTime").getValue(); var type=Ext.getCmp("type").getValue(); var discount=Ext.getCmp("discount").getValue(); var remark=Ext.getCmp("remark").getValue(); var json='{"oid":"'+id+'","cardName":"'+cardName+'","cardNum":"'+cardNum+'","maxConsumeSum":"'+maxConsumeSum+'","maxConsumeTimes":"'+maxConsumeTimes+'","validDays":"'+validDays+'","validTime":"'+validTime+'","type":"'+type+'","discount":"'+discount+'","remark":"'+remark+'"}'; Ext.Ajax.request({ url: 'saveCardType', headers: { 'userHeader': 'userMsg' }, method: 'POST', params:{data:json}, success: function (response, options) { // Ext.MessageBox.alert('成功', '从服务端获取结果: ' + response.responseText); var grid = Ext.create('MCS.basicOperation.cardType.cardTypeListGridPanel'); grid.getStore().load(); Ext.create('Ext.container.Viewport', { layout : 'fit', renderTo : Ext.getBody(), items : [ grid ] }); }, failure: function (response, options) { Ext.MessageBox.alert('失败', '请求超时或网络故障,错误编号:' + response.status); var grid = Ext.create('MCS.basicOperation.cardType.cardTypeListGridPanel'); grid.getStore().load(); Ext.create('Ext.container.Viewport', { layout : 'fit', renderTo : Ext.getBody(), items : [ grid ] }); } }); win.close(); }
在列表页面,我们定义了几个按钮,在“新增”和“修改”按钮中添加触发事件,当单击这两个按钮的时候,弹出“编辑卡类型”的表单。如下图
在列表界面的JS文件中,分别编写两个回调函数,一个是新增按钮的事件回调函数,一个是编辑按钮的事件回调函数。
addCardType: function(button, e, eOpts) { var window=Ext.create("MCS.basicOperation.cardType.editCardType",{type:1}); window.show(); }, editCardType: function(button, e, eOpts) { var record = Ext.getCmp("CardTypeGrid").getSelectionModel().getSelection(); if(record.length>0) { // debugger;//调试 var data=record[0].data; var window=Ext.create("MCS.basicOperation.cardType.editCardType",{type:2,data:data}); window.show(); }else { alert("请选择需要修改的信息!"); } },
在“新增”和“修改”修改按钮中触发这两个函数。
{ xtype: 'button', width: 36, text: '新增', listeners: { click: { fn: me.addCardType, scope: me } } }, { xtype: 'button', text: '编辑', listeners: { click: { fn: me.editCardType, scope: me } } },
定义一个Action,并且编写在一个方法用于处理客户提交过来的数据。
@Action(value="saveCardType",results={@Result(type="json")}) public String saveCardType(){ CardType ct=convertJSONToBean(CardType.class); cardTypeService.saveCardType(ct); return SUCCESS; }
上面我们用到了一个convertJSONToBean函数,这个函数是用来转换JSON数据格式的,在客户端中我们定义的参数的名称叫data(代码为:params:{data:json})。故在convertJSONToBean函数中也需要用Servlet API取出该参数。
/** * 将对象转换为BEAN * * @param clsz * @return */ public <T extends Object> T convertJSONToBean(Class<?> clsz) { HttpServletRequest request = ServletActionContext.getRequest(); String json = request.getParameter("data"); JSONObject jsonObject = JSONObject.fromObject(json); return (T) JSONObject.toBean(jsonObject, clsz); }