锁定老帖子 主题:关于复杂的业务表单数据保存和交互的推荐做法
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-03-04
这个题目有点让人迷惑,我就简单说明下,比如你要做一个表单数据保存的功能,这个表单(不是<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" /> A <input type="checkbox" value="2"/> B
这样我们可以在保存一个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(',');
}
}
}
}
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 1432 次