`

关于复杂的业务表单数据保存和交互的推荐做法

 
阅读更多

这个题目有点让人迷惑,我就简单说明下,比如你要做一个表单数据保存的功能,这个表单(不是<form>,只是代表业务上的概念)可能分4、5步骤,每一个步骤里可能有10-20甚至50个属性,其中有10个属性是列表形式,而且很多select/checkbox/radio/tr等dom对象都是根据业务规则动态生成的,同时后面的步骤的页面根据前些步骤的数据变化。。。==反正就是在javascript做OO建模也相当复杂就是。面对这样的需求,我把我们项目中的设计实现挑一些说下,有待改进处其他筒子请不吝指教。

 

关于最基本的和必要的oo建模,模块划分,html-js/css-js/event-js分离等就不多讲了,就是类似backbones的那种做法,先把数据、事件、控制、html视图等在组织上区分开,粒度根据需求去定。

 

1. 第一点就是json绑定优于模板渲染

传统上我们做一个crud,update的页面总是和create的页面多少有点不一样,这里的绑定的意思无非就是让样式、视图和数据更分离一点,这样做有个好处就是,不用在jsp/php/**template里面写类似下面的代码:

a)

<select>

{foreach item}

{if val = ***}

     selected option append

 

b)

<input name="" value="<c:out value="${savedVo.myProperty}" />"

 

c) 

<input if val eq ** checked />

 

尤其是存在一些关联菜单,需要和server动态交互的时候,要做这样的update页面,恐怕除了copy一个create页面外要写n多if/else了

 

其实可以采用下面做法,原语如下:

// 由数据对象绑定到表单控件集合

	bind: function(data, pre, skipFieldLl, _formContext){
		if(!data)
			return;
		pre = pre || 'f_';

		for(key in data){
			if(skipFieldLl && skipFieldLl.contains(key))
				continue;

			var val = data[key];
			var _target = $("[name='" + pre + key + "']", _formContext);

			if(_target.size() == 0)
				continue;

			// 下拉框如果不是特别说明需要绑定则跳过,因为大多数情况下拉框有联动的事件处理
			if(_target[0].nodeName == 'SELECT'){
				if(_target.hasClass('bind'))
					_target.val(val);
				else
					continue;
			}

			// 特殊处理,如果是单选
			if(_target.is(':radio')){
				_target.filter(function(){
					return $(this).val() == val;
				}).attr('checked', 'checked');
			// 如果是多选,val即是数组
			}else if(_target.is(':checkbox')){
				_target.filter(function(){
					return val.contains($(this).val());
				}).attr('checked', 'checked');
			// 一般情况就直接赋值
			}else{
				_target.val(val);
			}
		}
	}, 

 

关于关联菜单,可以先赋予第一个触发联动的那个select,然后再trigger('change')

然后被关联的在生成option时候,再判断是否是selected,如下:

 

// 先正常的去根据上级关联菜单去生成option

**.optionAppend(ll, _sel);
var targetVal = formData ? formData['saler_code'] : null;
_sel.val(targetVal);
 

2. 其次是通过约定把主要的数据关联关系确定,可以减少一些类似功能copy代码和dom标示

a) 比如对input[name$=_phone]的对象绑定一个特定的校验的事件;

b) 对input[name^=step1_ownerCode]在页面渲染后去执行val(getFormData('step1', 'ownerCode')),然后attr('disabled', true),表示要显示第一个步骤中录入的ownerCode字段,因为这里只是显示,所以还做做成readonly或disabled,如果ownerCode是一个select,而这里要显示的是option text里的业务有意义的字样,则可以看第三点的做法,一样使用;

c) 对一个tbody里的tr,该tr的td里是input/select,要保存这些数据,只需在tr里有一个有意义的标示就可以了,然后td里的控件值根据index下标去获取,然后再统一命名回来——这只是一个例子,适用的地方还很多,就是不需要大量的id/name等标示每一个或每一组控件,通过相对css选择器即可以达到目的,尤其是当这些控件是根据server端数据有规律动态渲染出来的情况。

 

3. 在保存表单数据键值对,可以同时保存一份业务字样的键值对

对于一个context(dom操作范围)里的html控件的数据保存,我们传统的做法是一个<form>wrap一下,然后$.param作为ajax的参数。其实我绝对如果只是一般的表单场景(非二进制数据流上传等),可以用一下原语更方面获取对象,然后以json形式传给server端再做数据绑定。

 

// 由表单控件集合转换成数据对象

	params: function(_inputs, pre){
		pre = pre || 'f_';
		var data = {};
		_inputs.each(function(){
			// 如果是checkbox or radio
			if($(this).is(':checkbox') || $(this).is(':radio')){
				if(!$(this).is(':checked'))
					return;
			}

			var val = $(this).val();
			// 有可能是select没有option
			if(val)
				val = val.trim();

			// 如果有空格则重新赋值
			if(this.nodeName == 'INPUT' || this.nodeName == 'TEXTAREA'){
				if(val != $(this).val())
					$(this).val(val);
			}

			var key = $(this).attr('name').substring(pre.length);

			// 如果是多选,以数组形式保存
			if($(this).is(':checkbox')){
				if(!data[key])
					data[key] = [];
				data[key].push(val);
			}else{
				data[key] = val;
			}
		});
		return data;
	}, 

 

 

其中pre只是命名的一个约定,比如在这个context中,我们只需要name^=f_开头的控件

其中一个是f_saler_code,另一个是f_saler_name,分别就表示业务员编号和姓名。

 

 

这里我们只是获取到了控件里的value=""的那部分值,如果完成这一步骤,我们想知道一个radio的value对应的业务字样(如性别M="男"),可以这么做:在动态生成或死代码写这个radio的html时候,我们尽量保持一样的格式,如

<input type="checkbox" value="1" />&nbsp;A&nbsp;<input type="checkbox" value="2"/>&nbsp;B&nbsp;

 

这样我们可以在保存一个context控件键值对时候同时保存一份业务字样的副本,代码如下:

 

getExtData: function(formData){
	for(key in formData){
		var _target = $("[name='" +pre + key + "']");
		if(_target.size() == 0)
			continue;

		if(_target[0].nodeName == 'SELECT'){
			extData[key] = _target.find('option:selected').text();
		}else if(_target.is(':radio') || _target.is(':checkbox')){
			// 用正则去匹配吧
			extData[key] = ***
			
			// 原语如下:
			if(_target.is(':radio')){
				var index = _target.index(_target.filter(':checked'));
				// checkbox/radio和其label都在一个tag里,一个parent
				var txt = _target.parent().html().trim();
				return ***.getLabelMatched(txt, index);
			}else if(_target.is(':checkbox')){
				var txt = _target.parent().html().trim();

				var ll = [];
				_target.each(function(index){
					if($(this).is(':checked')){
						var label = ***.getLabelMatched(txt, index);
						if(label)
							ll.push(label);
					}
				});
				if(ll.length > 0)
					return ll.join(',');
			}
		}
	}
}
 

 

 

 

 

分享到:
评论

相关推荐

    ssh(structs,spring,hibernate)框架中的上传下载

     其中save(FileActionForm fileForm)方法,将封装在fileForm中的上传文件保存到数据库中,这里我们使用FileActionForm作为方法入参,FileActionForm是Web层的表单数据对象,它封装了提交表单的数据。将...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    4、 系统的数据要求:1、数据录入和处理的准确性和实时性。2、数据的一致性与完整性。3、数据的共享与独立性。 2.2. 系统的可行性分析 2.2.1. 技术可行性 技术上的可行性分析要考虑将来要采用的硬件和软件技术...

    Eclipse_Swt_Jface_核心应用_部分19

    13.1.1 打印类(Printer)和打印数据类(PrinterData) 262 13.1.2 打印程序示例概述 265 13.1.3 打印程序示例:主窗口程序 265 13.1.4 打印程序示例:打开文件程序 268 13.1.5 打印程序示例:设置字体和...

    整理后java开发全套达内学习笔记(含练习)

    数值保存方式: 正数= 二进制 负数= 补码 补码= 反码 +1 正数=负数的补码(反码+1) 反码= 非(二进制数) 八进制数,零开头 011(八进制)=9(十进制) 十六进制数,零x开头 0x55(十六进制)=5*16+5(十进制) ...

    Spring面试题

    -(2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中; -(3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法; -(4)如果...

    2009计算机 毕业设计 诚信体育用品

    此模块和其他模块配合,将实现复杂的权限设置。权限管理包括:对栏目的访问权限、对某类信息的访问权限、对某个操作的许可权限;对网站管理员的权限也可通过此模块来控制,大型网站的维护需要多个管理员,一定需要对...

    java 面试题 总结

    JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变...

    超级有影响力霸气的Java面试题大全文档

     JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要...

    ASP.NET的网页代码模型及生命周期

    Cookie在客户端用户保存网站的少量的用户信息,服务器可以通过编程的方法获取用户信息,Cookie信息和页面请求通常一起发送到服务器,服务器对客户端传递过来的Cookie信息做处理。通常Cookie保存用户的登录状态、...

Global site tag (gtag.js) - Google Analytics