参考网址:
列表类的绑定: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%5B1%5D=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的字段名称不需要数组即可。