如何使用 jQuery 和验证在模态中制作 .NET MVC 表单

How to make a .NET MVC Form inside a Modal using jQuery with validation

我真的很难知道如何将所有这些放在一起。我已经在 .net MVC 页面中构建了很多次表单,无论是否进行验证。我已经使用 jQuery 构建了表单,有和没有验证。我已经在模态中构建了表单,但从未使用 MVC。

我从 original question 了解到,由于此表单位于模态中,因此我需要使用 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">&times;</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">&times;</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"
        }
    });
});