MVC 3 Razor @Html.ValidationMessageFor在通过jquery.load()局部加载时无法工作

14

我在这里组合了一个小例子,只是为了复制问题。 我有一个强类型的局部视图 _Name.cshtml:

@model ValidationInPartial.ViewModels.MyViewModel

<h2>@ViewBag.Message</h2>

    <fieldset>
        <legend>Name</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.MyName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.MyName)
            @Html.ValidationMessageFor(model => model.MyName)
        </div>

        <a href="#" id="reload">Reload Name</a>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

<script type="text/javascript">
    $(document).ready(function () {
        $("#reload").click(function () {
            $("#divName").load("Home/NameReload");
        });
    });
</script>

最初加载并显示在主 Index.cshtml 内的内容。

<div id="divForm">
    @using (Html.BeginForm()) {

        <div id="divName">
            @Html.Partial("_Name")
        </div>
    }
</div>

字段 MyName 是必需的,并且验证是通过 MyViewModel 中的 Required 属性实施的。

namespace ValidationInPartial.ViewModels
{
    public class MyViewModel
    {
        [Required(ErrorMessage = "Please enter a Name.")]
        public string MyName { get; set; }
    }
}

第一次加载页面后,如果您点击"创建"按钮并将字段留空,则验证消息"请填写名称。"会显示在该字段旁边,并且该字段本身会变成粉色,这是期望的行为。 现在通过点击“重新加载名称”链接(它会进行ajax调用 (jquery.load(...))),部分内容将被重新加载,这是控制器代码:

public PartialViewResult NameReload()
{
    MyViewModel myViewModel = new MyViewModel();
    ViewBag.Message = "Name Reloaded";
    return PartialView("_Name", myViewModel);
}

这一次,如果你点击“创建”按钮但是将字段留空,验证消息不会出现在字段旁边,尽管该字段变成了粉色。

原来,重新加载部分时,@Html.ValidationMessageFor不会像第一次那样呈现验证消息。

这是我使用的jquery文件:

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

我想知道这是Razor引擎渲染@Html.ValidationMessageFor的bug还是jquery的问题?有什么想法吗?

我也在某处读到过,ajax调用会丢失页面中的所有脚本,实际上我必须将任何javascript代码放在局部区域内,以便它们可以被再次渲染和使用。

与此同时,我找到了一个解决方法,即在局部视图中手动呈现@Html.ValidationMessageFor应该呈现的内容,即:

<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MyName"></span>

然而,这种解决方法意味着如果我们在ViewModel中更改验证的类型或验证消息,就需要修改视图中这个硬编码的html片段。

3个回答

18

除非您的字段包含在一个表单中,否则验证标记(span 标记、自定义字段属性等)不会被呈现。验证插件本身无法使用表单外的元素。

当 ASP.NET 渲染您的部分视图时,控件不在表单内,因此不会呈现这些元素。

在加载部分内容时,您需要使用 jQuery 选择器解析 HTML。

在我的示例中,父视图页面上有一个 TBODY 包含行。当我需要添加其他行时,我调用一个具有表单、表格、TBODY 和行集合的视图。

$.ajax({
    type: "POST",
    url: "/controller/action",
    data: ({Your: 'dataHere'}),
    dataType: "html",
    success:
        function(response){
            $('tbody').append($('tbody',$(response)).html());

            //The validation plugin can't bind to the same form twice.
            //We need to remove existing validators
            $('form').removeData("validator");

            //Refresh the validators
            $.validator.unobtrusive.parse(document);
        },
    error:
        function(){
            alert('An error occured while attempting to add the new content');
        }
});

请注意,我正在使用jQuery选择器来选择通过AJAX加载的View/PartialView中的行:
$('tbody',$(response)).html()

其余的包装器只是将来自AJAX视图/部分视图的行附加到调用方的tbody中:
$('tbody').append($('tbody',$(response)).html());

需要注意的是,在表单上运行验证插件后,如果不重新添加它,就不能再次调用它(参见jquery.validate.unobtrusive not working with dynamic injected elements)。

为了解决这个问题,我首先调用以下方法删除所有验证器:

$('form').removeData("validator");
$("form").removeData("unobtrusiveValidation");

我接下来使用以下方法刷新验证器:

$.validator.unobtrusive.parse(document);

18

@NickBork在这里给出了一个很好的答案。关键在于ASP.NET的MVC渲染引擎在没有表单的情况下不会输出验证脚本。例子中通过插入一个表单,然后选择返回的HTML内部部分来绕过此问题,本质上是丢掉了表单的外层包装。

还有另一种方法可以直接获取您的视图:

ViewContext.FormContext = new FormContext();
用这种方法,实际上不会输出FORM代码,但验证标记将存在。
谢谢, John

我在局部视图的顶部添加了这行代码,问题被解决了。最终如此简单。非常感谢。 - Roberto D
2
有时候添加“Developer has noted”这一行不起作用,比如使用AJAX获取部分视图。发生这种情况的原因是AJAX调用不会触发验证插件对页面上的表单进行“重新绑定”,这也是不应该的。在这些情况下,需要调用$.validator.unobtrusive.parse('#myPartialViewForm');方法。在我的情况下,我使用Fancybox来渲染一个表单,当用户点击“添加”按钮时。我的部分视图调用“$.validator.unobtrusive.parse('#wizardForm');”来配置部分视图的验证。 - Nick Bork
这绝对是正确的答案。其他人在处理 JQuery 和 AJAX 的不同问题(解析和重新绑定),这也是必需的,但单独使用不会有帮助。问题出在 MVC 上,如果没有它,它会认为通过 AJAX 返回的部分视图中有一个表单元素。 - John Fager
请注意,在您的部分视图 cshtml 的顶部添加以下代码行:@{ ViewContext.FormContext = new FormContext(); } - AaronLS
但在我的情况下,我已经在部分视图中使用了@Html.BeginForm()并遇到了同样的问题。给定的解决方案也不起作用。 - user1451111

2

我记不清我在哪里找到了解决方案。原因是你正在将PartialView加载到已经被jquery.validator.unobtrusive库解析的View中。你需要重新解析unobtrusive库。

    function ReparseValidation(){
       jQuery.validator.unobtrusive.parse("#yourcontainer");
    }

1
很抱歉,我已经尝试过了,但对我来说不起作用。问题不在于重新验证页面,而是@Html.ValidationMessageFor没有呈现验证消息,即上面示例中的<span>标签。 - Roberto D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接