如何使用 jQuery 和验证在模态中制作 .NET MVC 表单
How to make a .NET MVC Form inside a Modal using jQuery with validation
我真的很难知道如何将所有这些放在一起。我已经在 .net MVC 页面中构建了很多次表单,无论是否进行验证。我已经使用 jQuery 构建了表单,有和没有验证。我已经在模态中构建了表单,但从未使用 MVC。
我从 original question 了解到,由于此表单位于模态中,因此我需要使用 jQuery 来处理提交。我费了好大劲才想出如何将所有这些移动的部件组合在一起。到目前为止,我还没有找到将所有这些放在一起的教程(或教程组合)。
这是我需要的:
- 在我的 MVC 视图中有一个打开模式的按钮。 (这很好用。)
- 模式打开后,它包含一个带有多个文本字段和下拉列表的表单。每个都是必需的。 (为了使字段成为必填字段,我是否会在视图的模型中定义这些字段,就像我通常使用 MVC 表单那样?或者我是否在 jQuery 中创建需求?)
- 如果用户尝试提交表单,但表单为空或无效,模式将保持打开状态并显示验证消息。 (我从我的 original question 了解到,由于模式的原因,使用直接 MVC 是不可能的,并且需要一些 jQuery。我在这里迷路了。)
- 如果用户尝试提交表单并且所有字段都有效,则模式关闭,我们点击控制器并将字段保存到数据库中。 (不确定如何从 jQuery 中逃脱并直接点击普通控制器来处理最终逻辑。)
编辑:
杰森,谢谢你的帮助!根据您的建议,这就是我的工作方式。
父视图:
模态:
<div class="modal fade" id="AccountEditModal" tabindex="-1" role="dialog" aria-labelledby="AccountEditModalLabel">
<div class="modal-dialog modalAccountEdit" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h3><strong>Edit Account Profile - <span class="accountname"></span></strong></h3>
</div>
<div class="modal-body">
<div id="formContent">
@Html.Partial("_AccountProfileEdit", new GEDCPatientPortal.Models.AccountProfileEditViewModel())
</div>
</div>
</div>
</div>
</div>
然后是脚本:
@section Scripts {
<script>
$(document).ready(function () {
$('#AccountEditModal').on('shown.bs.modal', function () {
$('#myInput').focus()
})
$("#AccountEditModal").on("submit", "#form-accountprofileedit", function (e) {
e.preventDefault(); // prevent standard form submission
var form = $(this);
$.ajax({
url: form.attr("action"),
method: form.attr("method"), // post
data: form.serialize(),
success: function (partialResult) {
$("#formContent").html(partialResult);
}
});
});
});
</script>
}
局部视图(精简):
@using (Html.BeginForm("AccountProfileEdit", "Account", FormMethod.Post, new { id = "form-accountprofileedit", @class = "full-form" }))
{
@Html.CustomTextboxFor(model => model.Address)
<div style="text-align:right;">
<button type="submit" id="accountprofileedit-submit" name="accountprofileedit-submit" value="Edit Account" class="btn btn-primary" style="margin-left:5px;">Edit Account</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
}
控制器:
[HttpPost]
public ActionResult AccountProfileEdit(AccountProfileEditViewModel model)
{
if (ModelState.IsValid)
{
// logic to store form data in DB
}
return PartialView("_AccountProfileEdit", model);
}
考虑在模式 div 中放置一个 iframe,而不是渲染 partialView,这样您就可以像开发简单页面一样开发模式部分,提交、模型、必需等...
这样:
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modalAE" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h3><strong>Edit Account Profile - <span class="accountname"></span></strong></h3>
</div>
<div class="modal-body">
<iframe src='myApp/AccountProfileEdit'/>
</div>
<div class="modal-footer">
<button type="submit" id="accountprofileedit-submit" name="accountprofileedit-submit" value="Edit Account" class="btn btn-primary" style="margin-left:5px;">Edit Account</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
您可以使用内置的 MVC 验证脚本以及模型上的数据注释
public class AccountProfileEditViewModel
{
[Display(Name = "Address")]
[Required()]
[StringLength(200)]
public string Address { get; set; }
}
制作局部视图以保留模态形式。
_AccountProfileEdit.cshtml
@model AccountProfileEditViewModel
@using(Html.BeginForm("AccountProfileEdit", "Account",
FormMethod.Post, new { id = "form-accountedit-appt" }) {
@Html.ValidationSummary(true)
@Html.LabelFor(m => m.Address)
@Html.TextBoxFor(m => m.Address)
@Html.ValidationMessageFor(m => m.Address)
<button type="submit">Edit</button>
}
然后在您的模式框中引用它。如果你想要预填充的模型,你需要渲染一个动作:
<div class="modal-body" id="form-container">
@Html.Action("AccountProfileEdit", "Account", new { id=account.Id })
</div>
如果您只想要一个空白表格,那么您可以使用:
<div class="modal-body" id="form-container">
@Html.Partial("_AccountProfileEdit")
</div>
该操作使用 id
参数来获取和填充模型
[HttpGet]
public ActionResult AccountProfileEdit(int id)
{
AccountProfileEditViewModel model = db.GetAccount(id); // however you do this in your app
return PartialView("_AccountProfileEdit", model);
}
AJAX POST
现在您需要 AJAX 才能提交此表单。如果您依赖标准表单提交,浏览器将离开您的页面(并关闭您的模式)。
$("#myModal").on("submit", "#form-accountedit", function(e) {
e.preventDefault(); // prevent standard form submission
var form = $(this);
$.ajax({
url: form.attr("action"),
method: form.attr("method"), // post
data: form.serialize(),
success: function(partialResult) {
$("#form-container").html(partialResult);
}
});
});
您需要为提交事件使用事件委托$(staticParent).on(event, target, handler)
,因为稍后可能会替换表单内容。
Post动作
[HttpPost]
public ActionResult AccountProfileEdit(AccountProfileEditViewModel model)
{
// Request.Form is model
if (ModelState.IsValid)
{
// do work
return PartialView("_AccountEditSuccess");
}
return PartialView("_AccountProfileEdit", model);
}
客户端验证脚本应该阻止它们提交。但是,如果那以某种方式失败了,或者如果您无法在客户端上验证某些内容,那么您将拥有 ModelState.IsValid
。您也可以手动使服务器端的某些内容无效。
_AccountEditSuccess.cshtml
以及 "success" 部分视图。
<div>Success! <button>Click to close</button></div>
无效就是失败,对吧?
从你的 AJAX 成功处理程序你有
success: function(partialResult) {
$("#form-container").html(partialResult);
}
但这里的问题是我们不知道您得到的是 "success" 还是 "validation failure"。添加 error: function(err){ }
处理程序无济于事,因为验证失败被视为 HTTP 200 响应。在这两种情况下, div 内容都会被替换,用户将需要手动关闭模式。有 种方法可以传递额外的数据来区分这两种情况,但这是另一个很长的答案 post。
我在 2 个视图中使用 jQuery.validate() 构建 MVC Bootstrap 模态形式,“主”视图包含模态 div 和 Html.BeginForm(。 ..) 和具有表单元素的模态主体的局部视图,以及一个单独的 .js 文件,其中包含一个 jQuery 用于打开模态、绑定局部视图及其表单验证的方法。
它以主视图上 link 上的 CSS class 开始,调用 ActionResult() returns 局部视图,editChangeReason:
<a href="@Url.Action("_editCarrierChangeReason" ...)" class="editChangeReason">Add Exception</a>
来自控制器:
public ActionResult _editCarrierChangeReason(string reasonId)
{
...
return PartialView("modals/_modalEditCarrierChangeReason", rule);
}
在该主视图上,有一个典型的 Bootstrap 模态 div 添加了 @Html.BeginForm(...):
<!-- START Carrier Change Reason/Exception Edit Modal -->
<div id="editReason" class="modal fade in" data-backdrop="static" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
@using (Html.BeginForm("_editCarrierChangeReason", "TmsAdmin", new { area = "Tms" }, FormMethod.Post, new { id = "formCarrierChangeReason", autocomplete = "off" }))
{
<div id="editReasonContent">
</div>
}
</div>
</div>
</div>
<!-- END Carrier Change Reason/Exception Edit Modal -->
然后,它是包含表单元素的模态主体的简单局部视图:
@Html.HiddenFor(m => m.TypeId)
<!-- START Modal Body -->
<div class="modal-body">
<!-- START Carrier Exception Edit Form -->
<div class="form-row">
<div class="form-group col-6">
<label class="control-label">Code</label> <span class="required">*</span>
@Html.TextBoxFor(m => Model.Code, ...)
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label class="control-label">Description</label> <span class="required">*</span>
@Html.TextAreaFor(m => Model.Description, ...)
</div>
</div>
<!-- END Carrier Exception Edit Form -->
...
</div>
<!-- END Modal Body -->
对于 .js 文件,这有点复杂,但有一个逻辑流程。从 class、editChangeReason 中拾取按钮单击,打开模态。还有一个开关,在 $(document).ready() 之外处理 Bootstrap classes 管理模式并将部分视图绑定到“内容”div:
$(document).ready(function() {
// Carrier Change Reason/Exception
$("body").on("click", ".editChangeReason", function (e) {
e.preventDefault();
$("#editReasonContent").load(this.href,
function() {
$("#editReason").modal({
keyboard: true
},
"show");
bindForm(this, "editChangeReason");
return;
});
return false;
});
});
// Allows modals to use partial views when using <a data-modal=""...></a> on parent View:
function bindForm(dialog, type) {
$("form", dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
switch (type) {
case "editChangeReason":
if (result.success) {
$("#editReason").modal("hide");
location.reload();
} else {
$("#editReasonContent").html(result);
bindForm();
}
return;
default:
break;
}
}
});
return false;
});
}
最后,验证,也在 $(document).ready() 之外:
$("#formCarrierChangeReason").ready(function() {
$("#formCarrierChangeReason").validate({
rules: {
Code: {required: true},
Description: {required: true, minlength: 10}
},
messages: {
Code: "Please enter a unique code no longer than 10 characters",
Description: "Please add a description that explains this reason/exception"
}
});
});
我真的很难知道如何将所有这些放在一起。我已经在 .net MVC 页面中构建了很多次表单,无论是否进行验证。我已经使用 jQuery 构建了表单,有和没有验证。我已经在模态中构建了表单,但从未使用 MVC。
我从 original question 了解到,由于此表单位于模态中,因此我需要使用 jQuery 来处理提交。我费了好大劲才想出如何将所有这些移动的部件组合在一起。到目前为止,我还没有找到将所有这些放在一起的教程(或教程组合)。
这是我需要的:
- 在我的 MVC 视图中有一个打开模式的按钮。 (这很好用。)
- 模式打开后,它包含一个带有多个文本字段和下拉列表的表单。每个都是必需的。 (为了使字段成为必填字段,我是否会在视图的模型中定义这些字段,就像我通常使用 MVC 表单那样?或者我是否在 jQuery 中创建需求?)
- 如果用户尝试提交表单,但表单为空或无效,模式将保持打开状态并显示验证消息。 (我从我的 original question 了解到,由于模式的原因,使用直接 MVC 是不可能的,并且需要一些 jQuery。我在这里迷路了。)
- 如果用户尝试提交表单并且所有字段都有效,则模式关闭,我们点击控制器并将字段保存到数据库中。 (不确定如何从 jQuery 中逃脱并直接点击普通控制器来处理最终逻辑。)
编辑:
杰森,谢谢你的帮助!根据您的建议,这就是我的工作方式。
父视图:
模态:
<div class="modal fade" id="AccountEditModal" tabindex="-1" role="dialog" aria-labelledby="AccountEditModalLabel">
<div class="modal-dialog modalAccountEdit" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h3><strong>Edit Account Profile - <span class="accountname"></span></strong></h3>
</div>
<div class="modal-body">
<div id="formContent">
@Html.Partial("_AccountProfileEdit", new GEDCPatientPortal.Models.AccountProfileEditViewModel())
</div>
</div>
</div>
</div>
</div>
然后是脚本:
@section Scripts {
<script>
$(document).ready(function () {
$('#AccountEditModal').on('shown.bs.modal', function () {
$('#myInput').focus()
})
$("#AccountEditModal").on("submit", "#form-accountprofileedit", function (e) {
e.preventDefault(); // prevent standard form submission
var form = $(this);
$.ajax({
url: form.attr("action"),
method: form.attr("method"), // post
data: form.serialize(),
success: function (partialResult) {
$("#formContent").html(partialResult);
}
});
});
});
</script>
}
局部视图(精简):
@using (Html.BeginForm("AccountProfileEdit", "Account", FormMethod.Post, new { id = "form-accountprofileedit", @class = "full-form" }))
{
@Html.CustomTextboxFor(model => model.Address)
<div style="text-align:right;">
<button type="submit" id="accountprofileedit-submit" name="accountprofileedit-submit" value="Edit Account" class="btn btn-primary" style="margin-left:5px;">Edit Account</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
}
控制器:
[HttpPost]
public ActionResult AccountProfileEdit(AccountProfileEditViewModel model)
{
if (ModelState.IsValid)
{
// logic to store form data in DB
}
return PartialView("_AccountProfileEdit", model);
}
考虑在模式 div 中放置一个 iframe,而不是渲染 partialView,这样您就可以像开发简单页面一样开发模式部分,提交、模型、必需等...
这样:
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modalAE" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h3><strong>Edit Account Profile - <span class="accountname"></span></strong></h3>
</div>
<div class="modal-body">
<iframe src='myApp/AccountProfileEdit'/>
</div>
<div class="modal-footer">
<button type="submit" id="accountprofileedit-submit" name="accountprofileedit-submit" value="Edit Account" class="btn btn-primary" style="margin-left:5px;">Edit Account</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
您可以使用内置的 MVC 验证脚本以及模型上的数据注释
public class AccountProfileEditViewModel
{
[Display(Name = "Address")]
[Required()]
[StringLength(200)]
public string Address { get; set; }
}
制作局部视图以保留模态形式。
_AccountProfileEdit.cshtml
@model AccountProfileEditViewModel
@using(Html.BeginForm("AccountProfileEdit", "Account",
FormMethod.Post, new { id = "form-accountedit-appt" }) {
@Html.ValidationSummary(true)
@Html.LabelFor(m => m.Address)
@Html.TextBoxFor(m => m.Address)
@Html.ValidationMessageFor(m => m.Address)
<button type="submit">Edit</button>
}
然后在您的模式框中引用它。如果你想要预填充的模型,你需要渲染一个动作:
<div class="modal-body" id="form-container">
@Html.Action("AccountProfileEdit", "Account", new { id=account.Id })
</div>
如果您只想要一个空白表格,那么您可以使用:
<div class="modal-body" id="form-container">
@Html.Partial("_AccountProfileEdit")
</div>
该操作使用 id
参数来获取和填充模型
[HttpGet]
public ActionResult AccountProfileEdit(int id)
{
AccountProfileEditViewModel model = db.GetAccount(id); // however you do this in your app
return PartialView("_AccountProfileEdit", model);
}
AJAX POST
现在您需要 AJAX 才能提交此表单。如果您依赖标准表单提交,浏览器将离开您的页面(并关闭您的模式)。
$("#myModal").on("submit", "#form-accountedit", function(e) {
e.preventDefault(); // prevent standard form submission
var form = $(this);
$.ajax({
url: form.attr("action"),
method: form.attr("method"), // post
data: form.serialize(),
success: function(partialResult) {
$("#form-container").html(partialResult);
}
});
});
您需要为提交事件使用事件委托$(staticParent).on(event, target, handler)
,因为稍后可能会替换表单内容。
Post动作
[HttpPost]
public ActionResult AccountProfileEdit(AccountProfileEditViewModel model)
{
// Request.Form is model
if (ModelState.IsValid)
{
// do work
return PartialView("_AccountEditSuccess");
}
return PartialView("_AccountProfileEdit", model);
}
客户端验证脚本应该阻止它们提交。但是,如果那以某种方式失败了,或者如果您无法在客户端上验证某些内容,那么您将拥有 ModelState.IsValid
。您也可以手动使服务器端的某些内容无效。
_AccountEditSuccess.cshtml
以及 "success" 部分视图。
<div>Success! <button>Click to close</button></div>
无效就是失败,对吧?
从你的 AJAX 成功处理程序你有
success: function(partialResult) {
$("#form-container").html(partialResult);
}
但这里的问题是我们不知道您得到的是 "success" 还是 "validation failure"。添加 error: function(err){ }
处理程序无济于事,因为验证失败被视为 HTTP 200 响应。在这两种情况下, div 内容都会被替换,用户将需要手动关闭模式。有 种方法可以传递额外的数据来区分这两种情况,但这是另一个很长的答案 post。
我在 2 个视图中使用 jQuery.validate() 构建 MVC Bootstrap 模态形式,“主”视图包含模态 div 和 Html.BeginForm(。 ..) 和具有表单元素的模态主体的局部视图,以及一个单独的 .js 文件,其中包含一个 jQuery 用于打开模态、绑定局部视图及其表单验证的方法。
它以主视图上 link 上的 CSS class 开始,调用 ActionResult() returns 局部视图,editChangeReason:
<a href="@Url.Action("_editCarrierChangeReason" ...)" class="editChangeReason">Add Exception</a>
来自控制器:
public ActionResult _editCarrierChangeReason(string reasonId)
{
...
return PartialView("modals/_modalEditCarrierChangeReason", rule);
}
在该主视图上,有一个典型的 Bootstrap 模态 div 添加了 @Html.BeginForm(...):
<!-- START Carrier Change Reason/Exception Edit Modal -->
<div id="editReason" class="modal fade in" data-backdrop="static" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
@using (Html.BeginForm("_editCarrierChangeReason", "TmsAdmin", new { area = "Tms" }, FormMethod.Post, new { id = "formCarrierChangeReason", autocomplete = "off" }))
{
<div id="editReasonContent">
</div>
}
</div>
</div>
</div>
<!-- END Carrier Change Reason/Exception Edit Modal -->
然后,它是包含表单元素的模态主体的简单局部视图:
@Html.HiddenFor(m => m.TypeId)
<!-- START Modal Body -->
<div class="modal-body">
<!-- START Carrier Exception Edit Form -->
<div class="form-row">
<div class="form-group col-6">
<label class="control-label">Code</label> <span class="required">*</span>
@Html.TextBoxFor(m => Model.Code, ...)
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label class="control-label">Description</label> <span class="required">*</span>
@Html.TextAreaFor(m => Model.Description, ...)
</div>
</div>
<!-- END Carrier Exception Edit Form -->
...
</div>
<!-- END Modal Body -->
对于 .js 文件,这有点复杂,但有一个逻辑流程。从 class、editChangeReason 中拾取按钮单击,打开模态。还有一个开关,在 $(document).ready() 之外处理 Bootstrap classes 管理模式并将部分视图绑定到“内容”div:
$(document).ready(function() {
// Carrier Change Reason/Exception
$("body").on("click", ".editChangeReason", function (e) {
e.preventDefault();
$("#editReasonContent").load(this.href,
function() {
$("#editReason").modal({
keyboard: true
},
"show");
bindForm(this, "editChangeReason");
return;
});
return false;
});
});
// Allows modals to use partial views when using <a data-modal=""...></a> on parent View:
function bindForm(dialog, type) {
$("form", dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
switch (type) {
case "editChangeReason":
if (result.success) {
$("#editReason").modal("hide");
location.reload();
} else {
$("#editReasonContent").html(result);
bindForm();
}
return;
default:
break;
}
}
});
return false;
});
}
最后,验证,也在 $(document).ready() 之外:
$("#formCarrierChangeReason").ready(function() {
$("#formCarrierChangeReason").validate({
rules: {
Code: {required: true},
Description: {required: true, minlength: 10}
},
messages: {
Code: "Please enter a unique code no longer than 10 characters",
Description: "Please add a description that explains this reason/exception"
}
});
});