【问题标题】:How to make a .NET MVC Form inside a Modal using jQuery with validation如何使用带有验证的 jQuery 在 Modal 中制作 .NET MVC 表单
【发布时间】:2015-06-25 14:36:57
【问题描述】:

我真的很想知道如何将这一切放在一起。我已经多次在 .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">&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);

    }

【问题讨论】:

    标签: jquery asp.net-mvc forms modal-dialog


    【解决方案1】:

    您可以使用内置的 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),因为以后可能会替换表单内容。

    发布操作

    [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

    以及“成功”的局部视图。

    <div>Success! <button>Click to close</button></div>
    

    无效就是失败,对吧?

    来自您的 AJAX 成功处理程序

    success: function(partialResult) {
        $("#form-container").html(partialResult);
    }
    

    但这里的问题是我们不知道您得到的是“成功”还是“验证失败”。添加error: function(err){ } 处理程序无济于事,因为验证失败被视为HTTP 200 响应。在这两种情况下,div 内容都会被替换,用户需要手动关闭模式。 种方法可以传递额外的数据来区分这两种情况,但这是另一个长答案。

    【讨论】:

    • 杰森,首先,谢谢你!我真的很感激时间和帮助。第二,抱歉这么久才回复。我一直在度假。但这仍然是我需要解决的一个非常大的问题。第三,我根据您的建议对我的 OP 进行了编辑。我确定我错过了一些简单的东西,但它仍然无法正常工作。
    • 提交时导航通常意味着您没有正确阻止默认提交。确保没有任何脚本错误。将您的提交处理程序移动到$(document).ready() 内。作为健全性检查,删除 ajax 调用,您希望能够按下提交按钮而不发生任何事情。
    • 砰!将脚本移动到 $(document).ready() 中是最重要的!非常感谢!
    • 假设没有验证问题,如果我希望在表单提交后模式消失怎么办?我像您一样使用了代码,它除了将父页面加载到模态外还可以工作,因为成功函数(partialResult)将 html 加载到我的模态的 div 标记中。如果存在任何验证问题,我会尝试将用户保持在模式中,但更新数据库,关闭模式,然后转到我检查 TempData 以查看是否需要显示成功或失败消息的父级。
    • 听起来您正在将整个页面视图而不是部分视图加载到模式中。您应该使用您的代码创建一个新问题 - 可以更轻松地查看哪些内容不适用于您的示例。
    【解决方案2】:

    考虑在模态 div 中放置一个 iframe,而不是渲染部分视图,这样您就可以像开发简单页面一样开发模态部分,包括提交、模型、必需等...

    这样:

    <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>
    

    【讨论】:

    • 这是一个很好的解决方案。我确信它会触发我不以这种方式使用 iframe 的本能,但它适合 MVC 框架的自然方式使其引人注目。
    【解决方案3】:

    我在 2 个视图中使用 jQuery.validate() 构造 MVC Bootstrap 模态表单,“主”视图包含模态 div 和 Html.BeginForm(...),模态体的部分视图具有表单元素,带有一个单独的 .js 文件,该文件包含一个用于打开模式、绑定局部视图及其表单验证的 jQuery 方法。

    它以主视图上的链接上的 CSS 类开始,该类调用返回部分视图的 ActionResult(),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 文件,这有点复杂,但有一个逻辑流程。从类中选择按钮单击,editChangeReason,打开模式。在 $(document).ready() 之外还有一个开关,用于处理管理模式并将部分视图绑定到“内容”div 的 Bootstrap 类:

    $(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"
            }
        });
    });
    

    【讨论】:

    • 复活死者!我在 5 年和 2 份工作前问过这个问题 :) 我赞成你的答案,只是因为你付出了巨大的努力。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-15
    • 1970-01-01
    • 2013-09-10
    • 2020-10-18
    • 1970-01-01
    • 2012-12-09
    相关资源
    最近更新 更多