开发者

Why doesn't ValidationMessageFor display my validation message in this instance?

I've got a view model that contains 5 instances of a class as sub-properties. These sub-properties are rendered using a partial view, as follows:

<%Html.RenderPartial("_EntryItemForm", Model.EntryItem1, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "EntryItem1" } }); %>        
<%Html.RenderPartial("_EntryItemForm", Model.EntryItem2, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "EntryItem2" } }); %>
<%Html.RenderPartial("_EntryItemForm", Model.EntryItem3, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "EntryItem3" } }); %>
<%Html.RenderPartial("_EntryItemForm", Model.EntryItem4, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "EntryItem4" } }); %>
<%Html.RenderPartial("_EntryItemForm", Model.EntryItem5, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "EntryItem5" } }); %>

Within the partial view, I have the following (showing one field only):

<%: Html.LabelFor<EntryItemForm, string>(x => x.ItemName)%>
<%: Html.TextBoxFor<EntryItemForm, string>(x => x.ItemName)%>
<%: Html.ValidationMessageFor(x => x.ItemName)%>

The label and textboxes both render with the correct开发者_JAVA技巧 ids, names and so on, and the default model binder handles everything perfectly.

Unfortunately, even when the ModelState contains an error for the ItemName field, the ValidationMessage never appears. If I add a ValidationSummary to the parent view, the error is displayed. Normally I'd just use a ValidationSummary and move on, but the design I'm working to requires inline validation messages.

Does anyone have any idea why this might be?


For someone who are googling for this trouble:

Problem caused in a reason that ModelState doesn't passes to partial view in a right way when HtmlFieldPrefix used. This html helper solved the problem(for me) and validation errors displays correct:

public static void RenderPartialWithPrefix(this HtmlHelper helper, string partialViewName, object model, string prefix)
    {
        ViewDataDictionary WDD = new ViewDataDictionary {TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = prefix } };

        foreach(string key in helper.ViewData.ModelState.Keys)
        {
            if(key.StartsWith(prefix+"."))
            {
                foreach (ModelError err in helper.ViewData.ModelState[key].Errors)
                {
                  if(!string.IsNullOrEmpty(err.ErrorMessage))
                    WDD.ModelState.AddModelError(key, err.ErrorMessage);
                  if (err.Exception != null)
                      WDD.ModelState.AddModelError(key, err.Exception);
                }
                WDD.ModelState.SetModelValue(key, helper.ViewData.ModelState[key].Value);
            }
        }

        helper.RenderPartial(partialViewName,model,WDD);
    }

just use it for render partial views with prefix

@{Html.RenderPartialWithPrefix("_StructureEditSharePartView", Model.sharePart,"sharePart");}


Maybe the name of the field containing validation error in the metadata doesn't match the name of the field generated by the TextBoxFor helper. How about using editor templates? This way you don't need to bother with prefixes, setting template infos, problems with validation, ...

So you could define (~/Views/Shared/EditorTemplates/TypeOfEntryItem.ascx):

<%@ Control 
    Language="C#" 
    Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.TypeOfEntryItem>"
%>
<%= Html.LabelFor(x => x.ItemName) %>
<%= Html.TextBoxFor(x => x.ItemName) %>
<%= Html.ValidationMessageFor(x => x.ItemName) %>

and in the main view simply:

<% using (Html.BeginForm()) { %>
    <%= Html.EditorFor(x => x.EntryItem1) %>
    <%= Html.EditorFor(x => x.EntryItem2) %>
    <%= Html.EditorFor(x => x.EntryItem3) %>
    <%= Html.EditorFor(x => x.EntryItem4) %>
    <%= Html.EditorFor(x => x.EntryItem5) %>

    <input type="submit" value="OK" />
<% } %>

This even works with collections if you don't want to bother creating 5 properties on your model. You could have a simple property:

public IEnumerable<TypeOfEntryItem> EntryItems { get; set; }

and then:

<% using (Html.BeginForm()) { %>
    <%= Html.EditorFor(x => x.EntryItems) %>
    <input type="submit" value="OK" />
<% } %>

which will render the editor template for each item in the collection and of course take care of generating proper ids, names, ...


Adding the Html.ViewData object to the constructor of the ViewDataDictionary also works.

<%Html.RenderPartial("_EntryItemForm", Model.EntryItem1, new ViewDataDictionary(Html.ViewData) { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "EntryItem1" } }); %>

What this does is, it gives the current context (including the ModelState) to the newly created ViewDataDictionary, instead of giving it a separate context. This way, there is no need for an extension method.


http://www.dalsoft.co.uk/blog/index.php/2010/04/26/mvc-2-templates/#Complex_Types

Regarding your last question about IEnumerable, check out the article above for a decent workaround. Also, I wish someone would answer your question about making things work with the htmlFieldPrefix ... "elegant" or not, there's no point in supporting the display/rendering/processing of the view if you can't support the validation messages. I'm trying to get EditorFor to work for me as well (now) but if I ever get the time and return to making it work with RenderPartial, I'll come back here and post the solution.

For those who may find their way here by Googling the problem, the cause is that the ModelState contains keys like "ComplexObjectProperty.ModelProperty" but the ValidationMessageFor tries to look up just "ModelProperty". Major oversight.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜