MVC的表单(form)绑定到数据模型上

    技术2022-07-11  131

    参考网址:

    列表类的绑定:https://www.cnblogs.com/mrtiny/p/5807489.html js获取表单数据:https://www.jb51.net/article/90756.htm ajax提交表单数据:https://blog.csdn.net/CrackLibby/article/details/80013057

    在mvc中,表单的输入字段绑定到数据模型上的过程是自动完成的,本文进行简单地说明。

    注:我用的是vs2015,mvc5,测试用的是360浏览器

    我的目标是在一个页面上,弹出来一组输入项(form表单),用户在输入后点提交时,后台能够获取到输入的内容。如果所有处理成功,就刷新页面,使输入的内容更新到页面上(此时弹出的输入项会被隐藏),如果失败,就保持当前的状态。这样做有个好处是,如果失败,则当前的页面完成保持当前的输入状态,没有任务改变,也没有任何刷新,感受会好很多。

    为了实现这个目标,需要有几个步骤:

    1. 这个表单在页面加载时处于隐藏状态,在点击一个功能按钮时,显示这个表单。这个步骤简单,后面就不说了 2. 点击表单中的“提交”按钮时,用js获取表单的输入数据,然后使用ajax提交数据 3. 后台收到提交的数据时进行处理,并返回处理结果 4. ajax收到处理结果后,如果是失败就进行提示,如果成功,就刷新页面

    一个一个步骤来。

    2.1. 用js获取表单中输入的数据

    参数前面的网址,直接使用就行了,但是那个网址中的代码少了对select输入的处理,所以我这里对它进行了少许修改,修改后的代码如下:

    // https://www.jb51.net/article/90756.htm //获取指定form中的所有的<input>对象 function getFormElements(formId) { var form = Gho(formId); var elements = new Array(); var tagElements = form.getElementsByTagName('input'); for (var j = 0; j < tagElements.length; j++) { elements.push(tagElements[j]); } tagElements = form.getElementsByTagName('select'); for (var j = 0; j < tagElements.length; j++) { elements.push(tagElements[j]); } return elements; } //获取单个input中的【name,value】数组 function checkboxSelector(element, hoId) { if (element.checked) return [hoId, element.value]; } // 单个select的选中项 function selectSelector(element, hoId) { for (var i = 0; i < element.length; i++) { if (element[i].selected == true) return [hoId, element[i].value]; } } function getFormInput(element) { var hoId = element.name; if (hoId == null || hoId == "") hoId = element.id; if (hoId == null || hoId == "") { return null; } switch (element.type.toLowerCase()) { case 'submit': case 'hidden': case 'password': case 'text': return [hoId, element.value]; case 'checkbox': case 'radio': return checkboxSelector(element, hoId); case 'select-one': return selectSelector(element, hoId); } return false; } /* 组合URL */ function serializeElement(element) { var method = element.tagName.toLowerCase(); var parameter = getFormInput(element); if (parameter == null) return; if (parameter) { var key = encodeURIComponent(parameter[0]); if (key.length == 0) return; if (parameter[1].constructor != Array) parameter[1] = [parameter[1]]; var values = parameter[1]; var results = []; for (var i = 0; i < values.length; i++) { results.push(key + '=' + encodeURIComponent(values[i])); } return results.join('&'); } } //调用方法 function serializeForm(formId) { var elements = getFormElements(formId); var queryComponents = new Array(); for (var i = 0; i < elements.length; i++) { var queryComponent = serializeElement(elements[i]); if (queryComponent) queryComponents.push(queryComponent); } return queryComponents.join('&'); }

    除了添加selectSelector()之外,对函数的名称进行了修改,key由单一的元素名称修改成了元素的名称和ID号二选一。

    调用serializeElement()之后,输出的就是用 & 号连接起来的经过url编码的键值对了,所以它可以直接放在 url 的后面做为参数,即 http://serverip/mytask?<formdata> 其中的 <formdata> 表示 serializeElement() 的返回数据

    2.2 用ajax提交数据

    先看js代码:

    function AddProperty_Submit() { var formData = ""; formData = serializeForm("prpAddForm"); var errMsg = ""; var curUrl = GetPageUrlBase(false); var urlString = curUrl + 'AddProperty'; urlString += "?" + formData; $.ajax( { type: "POST", url: urlString, contentType: "text/javascript", // request的数据类型是json dataType: "json", // 返回的数据类型是json //data: formData, beforeSend: function () { // 发送之前会进入到这个函数中,如果返回了false,就不会发送了 //return false; return true; }, success: function (data) { // 由于上面设置了"json",所以这里的data就是一个json对象了,服务器返回{}时,这里不会进到此函数中 // 所以一旦进来,就说明确实拿到了标定的json数据 g_isAddSubmited = false; if (data == null || data == "" || data == "{}") { errMsg = "未知错误,请重试"; alert(errMsg); return; } if (data["opeResult"] == 0) { // 提交成功,就刷新页面 location.reload(true); return; } // 失败 errMsg = data["errmsg"]; alert(errMsg); }, error: function (errMsg) { console.log(errMsg); } }); }

    本来我以为应该是要把表单数据放在 data 项中,但实际上我那样它不成功,而是应该放在url的参数里。最后的url类似这样的格式: http://serverip/AddProperty?name=aa&defval=bb

    其中 AddProperty 是control中的对应action,由于这个action是在js中指定的,所以html表单中指定的action名称就没啥重要的了。

    这里要说明的一个情况,因为这个表单是在一个页面上的,而这个页面上可能存在多个表单,还有就是这个页面绑定的数据模型不是这个表单所要求的,同时因为多个表单也不可能强绑定多个数据模型,所以从表单上来说,没办法直接指定要绑定的数据模型。

    3. 后台对数据的处理

    为了方便,action的参数还是要采用数据模型会方便很多,对程序维护会非常有利,所以还是往这方面去靠。

    经过上面的网址来看,似乎并不难,只要数据模型的字段名称和表单的字段表名一样就可以了。数据模型定义如下(其实这里应该简化一下能够说明问题就行了,但那样要改来改去的,所以我就把这个输入的所有内容全部贴出来了,省得改):

    public class PropertyAddViewModeo { // 添加列表类的绑定值:https://www.cnblogs.com/mrtiny/p/5807489.html // 只对修改有效 public int prpId { get; set; } [Display(Name = "显示名称")] public string prpDesc { get; set; } // 键名 [Display(Name = "字段名称")] public string prpName { get; set; } // 输入方式 public int prpSubType { get; set; } public string prpValues { get; set; } public string prpDefValue { get; set; } public List<int> toPrpGroup { get; set; } }

    这里除了最后一个toPrpGroup之外,其它的都简单,表单如下代码(注:为了减少代码,这里简化了一些无关的辅助内容,以及排版代码):

    <div id="divPrpAdd" style="display:none;position:absolute;z-index:1;background-color:honeydew;width:800px;min-height:600px;border:1px solid black;padding:8px;"> @using (Html.BeginForm("AddProperty", "Manage", FormMethod.Post, new { id = "prpAddForm" })) { <input type="hidden" name="prpId" id="prpId" value="" /> <label>属性名称:</label> <input type="text" name="prpDesc" id="prpDesc" /> <label>字段名称:</label> <input type="text" name="prpName" id="prpName" /> <label>输入方式:</label> <select name="prpSubType" id="prpSubType"> <option value="0">输入</option> <option value="1" selected>选择</option> </select> <label>属性值列表:</label> <input type="text" name="prpValues" id="prpValues" /> <label>缺省值:</label> <input type="text" name="prpDefValue" id="prpDefValue" /> <label>加入到属性组:</label> <div id="prpGroupList"> <input type="checkbox" id="toPrpGroup[0]" name="toPrpGroup[0]" value="3"> <input type="checkbox" id="toPrpGroup[1]" name="toPrpGroup[1]" value="4"> </div> <input type="button" style="min-width:80px;" value="确定" onclick="AddProperty_Submit();" /> <input type="button" style="min-width:80px;" value="取消" onclick="AddProperty_Cancel();" /> } </div>

    这里注意的是最后的checkbox,它实际上是动态生成的,其个数不确定,但至少会有一个。为了和数据模型的绑定,参考上面的网址,其id采用数组的方式,名称和数据模型中的字段名称也保持一致,均为 toPrpGroup。

    在action中的处理函数仅用于测试,代码如下:

    [HttpPost] public string AddProperty(PropertyAddViewModeo model) { return CreateResultString(1, "组名称已经存在了,不允许重复"); }

    不看处理代码,只看参数是否获取到了表单数据,当界面输入如下:

    这里注意最后的两个选择框,acion的model参数内容如下所示:

    获取的这些数据全都没错,现在不选“车牌相关”而只选中“司机相关”,则看获取到的数据如下:

    这样就没有数据了,但是从表单中的确实获取到它的值了,看js中formData变量的值:"prpId=-1&prpDesc=aa&prpName=bb&prpValues=cc&prpDefValue=dd&toPrpGroup[1]=4&prpSubType=1"

    这就有点麻烦了。对于数据模型的定义,我也参考上面的那个网址添加了构造函数并在其中对toPrpGroup进行了初始化new,通过多次尝试,这个初始化对结果没有任何影响,也就是说,是否有这个初始化,结果都是一样的。

    从这里看,如果是从0开始选择,是可以正常获取到值的,但如果不是从0开始,就获取不到它的值,同样,我估计如果中间有没选中的,则后面的值也获取不到。

    如果只是这个不能获取到值,就显然有点不爽,于是就把数据的名称改掉,如下所示:

    <input type="checkbox" id="toPrpGroup" name="toPrpGroup" value="3"> <input type="checkbox" id="toPrpGroup" name="toPrpGroup" value="4">

    这样改过之后,即名称不再使用数组的方式,结果就可以了,此时,如果两个都选中,则formData的值如下:

    "prpId=-1&prpDesc=aa&prpName=bb&prpValues=cc&prpDefValue=dd&toPrpGroup=3&toPrpGroup=4&prpSubType=1"

    有两个toPrpGroup参数,这样看,如果有多个值的,都可以采用这个方法,即数据模型采用 List 数据类型,form的字段名称不需要数组即可。

    Processed: 0.017, SQL: 9