差不多一年没更新博客了,最近上班的公司提倡大家做技术分享,再加上今天早上看一个大佬的博文的时候,他明确提到,作为一个程序员,最好是养成写博文的习惯,一是可以总结自己的知识点,提升个人能力,二是写得多了,能提升个人影响力。个人影响力的事就不说了,本人都没影响力,何来提升,觉得第一点说得也对,闲来无事,就随便写写吧。
公司最近在用layui做前端,但因为前端人员紧张问题,就把新项目的前后端都扔给了后端工作人员。一同事在做表单提交时,遇到了一个麻烦的问题,就是他的表单要填的内容特别多,而每一项都需要验证,所以他就用了layui的表单验证,代码就自然而然的变成了下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
// html部分 <form class="layui-form"> <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"> <legend>专家详情</legend> <div class="layui-form-item"> <label class="layui-form-label">专家姓名</label> <div class="layui-input-inline"> <input type="text" name="name" maxlength="30" lay-verify="required" placeholder="请输入专家姓名" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">年龄</label> <div class="layui-input-inline"> <input type="number" name="age" oninput="changeAge(this)" lay-verify="required|checkAge" placeholder="请输入年龄" autocomplete="off" class="layui-input"> </div> <label class="layui-form-label">活跃度</label> <div class="layui-input-inline"> <input type="number" name="activeRate" maxlength="4" lay-verify="required" placeholder="请输入活跃度" class="layui-input"> </div> </div> <div class="layui-form-item layui-form-text"> <label class="layui-form-label">详细信息</label> <div class="layui-input-block"> <textarea style =" width: 500px;" maxlength="400" lay-verify="required" name="intro" placeholder="请输入内容" class="layui-textarea"></textarea> </div> </div> </fieldset> <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"> <legend>工作情况</legend> <div id="monthList"> <div class="layui-form-item"> <label class="layui-form-label">一月</label> <div class="layui-input-block"> <input type="text" style="width: 500px;" name="one" maxlength="50" autocomplete="off" placeholder="请输入本月工作情况,如果没有默认为无" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">二月</label> <div class="layui-input-block"> <input type="text" style="width: 500px;" name="two" maxlength="50" autocomplete="off" placeholder="请输入本月工作情况,如果没有默认为无" class="layui-input"> </div> </div> </div> </fieldset> <div class="page-footer"> <div class="btn-list"> <div class="btnlist"> <button class="layui-btn" lay-submit="submit" lay-filter="submit">保存</button> <span class="layui-btn">返回</span> </div> </div> </div> </form> // js部分 layui.use(['form', 'layer','jquery','upload','formSelects'], function () { var layer=layui.layer,form = layui.form,upload = layui.upload,formSelects=layui.formSelects; //监听提交 form.on('submit(submit)',function(data){ var obj = data.field; $.ajax({ url:'/expert/user/add', contentType: "application/json", data: obj, type:'post', success: function (R) { }, error: function () { } }); return false; }); }); |
原本的表单比这个复杂得多,我这里由于是演示代码,只提了一部分出来。
说一下问题吧,就是在表单提交时,我们可以通过data.field拿到表单的各个输入框的值,data.field是一个对象,key为input的name,值就为对应input的值。本来这对于一般的表单是没什么问题的,但一旦遇到某一个key的值需要是数组或对象(也就是存在二级对象)的时候,就不好使了,就上面的表单而言,后台需要的数据格式应该是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 |
data = { expertUser:{ name: '', age: '', activeRate: '', intro: '' }, expertUserWorkList:{ one:'', two:'' } } |
也就是工作情况和个人信息是分装在两个不同的对象里的,毕竟这样更符合常规。
而通过data.field拿到的数据却是这样的:
1 2 3 4 5 6 7 8 |
data = { name: '', age: '', activeRate: '', intro: '', one:'', two:'' } |
这就让人头疼了,必须得自己再重新处理一遍,像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var data = data.field; var obj = { expertUser:{ name: data.name, age: data.age, activeRate: data.activeRate, intro: data.intro }, expertUserWorkList:{ one: data.one, two: data.two } } |
这样做最大的问题就在于,键值对少还能接受,但一旦键值对多了,写得就很烦人了,像我同事的表单,专家信息就十几个字段,工作情况是一月到十二月,还有专家标签,等等,共几十个字段,如果都这样写,不仅写的人会觉得累,代码看上去也是不太优雅。这不,我就是因为同事找我抱怨说这样太麻烦,才有了这篇文章嘛。他的意思是能不能做到这样一个功能,就是给input的name设置一个可分割的值,让代码根据name自行解析对象,html这样写(注意name属性):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<form class="layui-form"> <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"> <legend>专家详情</legend> <div class="layui-form-item"> <label class="layui-form-label">专家姓名</label> <div class="layui-input-inline"> <input type="text" name="expertUser.name" maxlength="30" lay-verify="required" placeholder="请输入专家姓名" autocomplete="off" class="layui-input"> </div> </div> </fieldset> <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"> <legend>工作情况</legend> <div id="monthList"> <div class="layui-form-item"> <label class="layui-form-label">一月</label> <div class="layui-input-block"> <input type="text" style="width: 500px;" name="expertUserWorkList.one" maxlength="50" autocomplete="off" placeholder="请输入本月工作情况,如果没有默认为无" class="layui-input"> </div> </div> </div> </fieldset> <div class="page-footer"> <div class="btn-list"> <div class="btnlist"> <button class="layui-btn" lay-submit="submit" lay-filter="submit">保存</button> <span class="layui-btn">返回</span> </div> </div> </div> </form> |
拿到的对象就是这样,
1 2 3 4 5 6 7 8 |
data = { expertUser:{ name: '' }, expertUserWorkList:{ one:'' } } |
他也确实那样试过了,然而拿到的对象却是这样的:
1 2 3 4 |
data = { "expertUser.name":"xxx", "expertUserWorkList.one":"xxx" } |
这里要说句题外话,其实在html5中,原生的表单是提供了嵌套对象的值绑定的,你只需像这样绑定name属性即可:
1 2 3 |
<input id="startDate" name="expertUser[name]" required="required"> <input id="endDate" name="expertUser[age]" required="required"> <input id="logArea" name="expertUserWorkList[one]" required="required"> |
但是在layui中如上方式却不行(个人认为,在layui中这种方式行不通的原因,是它返回的data.field是被进行了处理的)。
对于下面这种对象:
1 2 3 4 |
data = { "expertUser.name":"xxx", "expertUserWorkList.one":"xxx" } |
我首先想到的就是通过拆分key的值,对他进行二次解析,也就是本文所说的智能方法,实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function parseFormData(data){ var obj = JSON.parse(JSON.stringify(data)); for(var key in obj){ if(key.indexOf('.') > -1){ var keys = key.split('.'); if(!obj[keys[0]]){ obj[keys[0]] = {}; } obj[keys[0]][keys[1]] = obj[key]; delete obj[key]; } }; return obj; } |
效果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
data = { "expertUser.name":"xxx", "expertUserWorkList.one":"xxx" } var obj = parseFormData(data); console.log(obj); // { // expertUser: { // name: 'xxx' // }, // expertUserWorkList: { // one: 'xxx' // } // } |
其实,针对于只有两级的情况,上面这个方法的实现已经比之前好很多了,只需调用一下方法转换就行,不用一个个的自己写。但上述方法针对于3级的情况,就不行了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
data = { "expertUser.name.first":"xxx", "expertUserWorkList.one.prev":"xxx" } var obj = parseFormData(data); console.log(obj); // { // expertUser: { // "name.first": 'xxx' // }, // expertUserWorkList: { // "one.prev": 'xxx' // } // } |
不行的原因也很简单,因为上面的方法没做多级处理。于是,同事又开始问了,能不能做个多级的。而我也在想,既然做都做了,就做得更好一点,指不定以后自己也能派上用场,再说了,练一下手也行啊。然而,在真正开始写的时候,才发现了不是想的那么简单,一旦超过两级,就不说拆分字段那么简单了,要考虑值覆盖问题,循环的边界问题,最重要的一点,是必须得用到递归。读到这里的朋友,如果感兴趣,不妨先别往下看,自己尝试一下,看多久能写出来,反正我是花了差不多半个小时才搞定,还是直接上代码吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
function parseFormDataM(obj) { function extendObj(src,target){ for(var key in target){ src[key] = target[key] } } for (var key in obj) { if (key.indexOf('.') > 0) { var len = key.split('.').length; if (len <= 2) { var keys = key.split('.'); key0 = keys[0],key1 = keys[1]; if (!obj[key0]) { obj[key0] = {}; }; if(obj[key0][key1]){ extendObj(obj[key0][key1],obj[key]) }else{ obj[key0][key1] = obj[key]; } delete obj[key]; } else { var index = key.indexOf('.'); var key0 = key.slice(0, index), key1 = key.slice(index + 1); if(!obj[key0]){ obj[key0] = {} } if(obj[key0][key1]){ extendObj(obj[key0][key1],obj[key]) }else{ obj[key0][key1] = obj[key]; } delete obj[key]; if(key1.indexOf('.')>0){ parseFormDataM(obj[key0]); } } } } return obj; } |
测试结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var data2 = { 'aa.bb.cc':111, 'aa.bb':{ dd:222, ee:{ ff:333, gg:444 } }, 'aa.bb.ff':555 } console.log(parseFormDataM(data2)) // { // aa:{ // bb:{ // cc:111, // dd:222, // ee:{ // ff:333, // gg:444 // }, // ff:555 // } // } // } |
上面的代码可以让大家看清解析逻辑,我们可以适当的修改一下判断条件,并做一下公共代码提取,最后得到一个精简版,下载地址:点击这里下载。好了,本文到此结束,如果觉得本文对你有帮助,下载时记得给个star哦。有兴趣的朋友,也可以思考一下,如果属性值是一个对象,而对象里的key值还有aa.bb这样的情况,该如何处理?
发表评论