MVC 3 Razor @Html.ValidationMessageFor 无法通过 jquery.load() 部分加载
我在这里整理了一个小例子只是为了重现这个问题。 我有一个强类型的部分视图 _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 的方式中的错误,或者是 jquery 的问题? 知道为什么会发生这种情况吗?
我还在某处读到,ajax 调用会丢失页面的所有脚本,事实上,我必须将任何 javascript 代码保留在部分中,以便可以再次渲染和使用它们。
与此同时,我找到了一种解决方法,即在部分中手动渲染应该由 @Html.ValidationMessageFor 渲染的内容,即:
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MyName"></span>
但是,此解决方法意味着如果我们更改验证类型或仅更改必需属性内的验证消息在ViewModel中,我们需要修改视图中这段硬编码的html。
I have put together a small example here just to replicate the problem.
I have a strongly typed partial view _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>
that is initially loaded and displayed inside the main Index.cshtml
<div id="divForm">
@using (Html.BeginForm()) {
<div id="divName">
@Html.Partial("_Name")
</div>
}
</div>
The field MyName is required and validation is implemented through Required attribute in MyViewModel
namespace ValidationInPartial.ViewModels
{
public class MyViewModel
{
[Required(ErrorMessage = "Please enter a Name.")]
public string MyName { get; set; }
}
}
After the page is loaded the first time, if you click the Create button leaving the field empty the validation message "Please enter a Name." shows beside the field and the field itself turns pink, which is the expected behaviour.
Now by clicking the "Reload Name" link, which makes an ajax call (jquery.load(...)), the partial is reloaded, here is controller code:
public PartialViewResult NameReload()
{
MyViewModel myViewModel = new MyViewModel();
ViewBag.Message = "Name Reloaded";
return PartialView("_Name", myViewModel);
}
This time if you click the Create button leaving the field empty the validation message does not appear beside the field, although the field turns pink.
It turns out that when reloading the partial the @Html.ValidationMessageFor doesn't render the validation message as the first time.
Here is the jquery files I use
<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>
I wonder if this is a bug in the way the Razor engine renders the @Html.ValidationMessageFor or is that a problem with jquery?
Any idea why this happens?
I have also read somewhere that the ajax call looses all the scripts for the page, in fact I have to keep any javascript code inside the partial so that they can be rendered and used again.
In the meantime I found a workaround which is to manually render in the partial what was supposed to be rendered by @Html.ValidationMessageFor which is:
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MyName"></span>
However this workaround means that if we change the type of validation or just the validation message inside the Required attribute in the ViewModel, we need to modify this hard-coded piece of html in the view.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
@NickBork 这里有一个很好的答案。关键是ASP.NET的MVC渲染引擎如果认为不存在表单就不会输出验证脚本。给出的示例是黑客将其放入表单中,然后从中选择 HTML 的内部部分,然后返回,实质上丢弃了表单的外部包装。
还有另一种方法可以让您获得视图:
使用这种方法,实际上不会有 FORM 代码输出,但会有验证标记。
谢谢,
约翰
@NickBork has a great answer here. The key is that ASP.NET's MVC rendering engine does not output the validation script if it doesn't think that there is a form. The example given hacks it buy putting in a form and then selection an inner section of HTML from was was returned, essentially throwing the outer wrapper of the form away.
There is another method so that you can just get your view:
With this method, there won't actually be FORM code output, but the validation markup will be there.
Thanks,
John
除非您的字段包含在 FORM 中,否则不会呈现验证标记(跨度标记、自定义字段属性等)。验证插件本身不适用于表单之外的元素。
当 ASP.NET 呈现部分视图时,控件不在表单内,因此不会呈现元素。
当您加载部分内容时,您需要使用 jQuery 选择器来解析 HTML。
在下面的示例中,我在包含行的父视图页面上有一个 TBODY。当我需要添加其他行时,我会调用一个包含表单、表、tbody 和行集合的视图。
请注意,我使用 jQuery 选择器来选择使用 AJAX 加载的 View/PartialView 内部的行:
包装器的其余部分只是将 AJAX View/PartialView 中的行附加到调用父级
tbody
:还有一些注意事项,在表单上运行验证器插件后,如果不重新添加它就无法再次调用它(请参阅jquery.validate.unobtrusive 不适用于动态注入元素)
为了解决这个问题,我首先调用以下方法来删除所有验证器:
然后使用以下命令刷新验证器:
Validation markup (span tags, custom field attributes, etc) are not rendered unless your fields are contained within a FORM. The validation plugin itself does not work with elements outside of a form.
When ASP.NET renders your Partial View the controls are not within a form and thus do not get the elements rendered.
When you load you're partial content you'll need to parse the HTML using a jQuery selector.
In my sample below I have a TBODY on the parent View page that contains rows. When I need to add additional rows, I make a call to a View which had a form, table, tbody and collection of rows.
Note that I'm using a jQuery selector to select the rows that are inside of the View/PartialView that are loaded in by using AJAX:
The rest of the wrapper just appends the rows from the AJAX View/PartialView to the calling parents
tbody
:A couple other notes, after the validator plugin has been run on a form, it can not be called again without re-adding it (see jquery.validate.unobtrusive not working with dynamic injected elements)
To fix this, I first call the following method to remove all validators:
I then refresh the validators using the following:
我不记得在哪里找到了解决方案。原因是因为您正在将 PartialView 加载到已由 jquery.validator.unobtrusive 库解析的视图中。您需要重新解析不显眼的库
I can't remember where I found the solution. The reason is because you are loading a PartialView into a View that has already been parsed by the jquery.validator.unobtrusive library. You need to re-parse the unobtrusive library