Friday 8 July 2016

Forcing ValidationSummary to include property errors from IValidatableObject

I implemented IValidatableObject to my class and intended to have the errors shown in @Html.ValidationSummary(true). However, the class with the IValidatableObject  is a property of the model in my view, and that causes the errors to be hidden when I exclude property errors.
There is another person on Stackoverflow with exactly the same problem, but no one has answer. http://stackoverflow.com/questions/6433023/mvc3-validationsummary-exclude-property-errors-ivalidatableobject/10433504.
To solve this, I dug out MVC3’s source code, copied and changed the ValidationSummary method to allow specific properties to be included. The following shows how to use my ValidationSummary method
@Html.ValidationSummary(new [] { "PropertyName" })
would include the property named PropertyName
@Html.ValidationSummary(new [] { "ArrayName[]" })
would include the properties ArrayName[0], ArrayName[1] etc.
@Html.ValidationSummary(new [] { "ArrayName[]", "PropertyName" })
would include both.
And here’s the code!
public static MvcHtmlString ValidationSummary(thisHtmlHelper htmlHelper, string[] includePropertyErrors)
{
    return ValidationSummary(htmlHelper, includePropertyErrors, nullnull);
}

public static MvcHtmlString ValidationSummary(thisHtmlHelper htmlHelper, string[] includePropertyErrors,string message)
{
    return ValidationSummary(htmlHelper, includePropertyErrors, message, null);
}

public static MvcHtmlString ValidationSummary(thisHtmlHelper htmlHelper, string[] includePropertyErrors,string message, IDictionary<stringobject>htmlAttributes)
{
    if (htmlHelper == null)
    {
        throw new ArgumentNullException("htmlHelper");
    }

    FormContext formContext =htmlHelper.ViewContext.ClientValidationEnabled ?htmlHelper.ViewContext.FormContext : null;
    if (htmlHelper.ViewData.ModelState.IsValid)
    {
        if (formContext == null)
        {  // No client side validation
            return null;
        }

        // TODO: This isn't really about unobtrusive; can we fix up non-unobtrusive to get rid of this, too?
        if(htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
        {  // No client-side updates
            return null;
        }
    }

    string messageSpan;
    if (!string.IsNullOrEmpty(message))
    {
        TagBuilder spanTag = new TagBuilder("span");
        spanTag.SetInnerText(message);
        messageSpan =spanTag.ToString(TagRenderMode.Normal) +Environment.NewLine;
    }
    else
    {
        messageSpan = null;
    }

    StringBuilder htmlSummary = new StringBuilder();
    TagBuilder unorderedList = new TagBuilder("ul");

    IEnumerable<ModelState> modelStates = from ms inhtmlHelper.ViewData.ModelState
                                            where ms.Key== htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix ||
                                               includePropertyErrors.Any(property =>
                                                {
                                                   string prefixedProperty =string.IsNullOrEmpty(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix) ? property : htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix + "." +property;
                                                    if(property.EndsWith("[]"))
                                                    {
                                                       return prefixedProperty.Substring(0, property.Length - 2)== Regex.Replace(ms.Key, @"\[[^\]]+\]"string.Empty);
                                                    }
                                                    else
                                                    {
                                                       return property == ms.Key;
                                                    }
                                                })
                                            selectms.Value;

    if (modelStates != null)
    {
        foreach (ModelState modelState in modelStates)
        {
            foreach (ModelError modelError inmodelState.Errors)
            {
                string errorText =GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError);
                if (!String.IsNullOrEmpty(errorText))
                {
                    TagBuilder listItem = newTagBuilder("li");
                    listItem.SetInnerText(errorText);
                   htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
                }
            }
        }
    }

    if (htmlSummary.Length == 0)
    {
        htmlSummary.AppendLine(@"<li style=""display:none""></li>");
    }

    unorderedList.InnerHtml = htmlSummary.ToString();

    TagBuilder divBuilder = new TagBuilder("div");
    divBuilder.MergeAttributes(htmlAttributes);
   divBuilder.AddCssClass((htmlHelper.ViewData.ModelState.IsValid) ? HtmlHelper.ValidationSummaryValidCssClassName :HtmlHelper.ValidationSummaryCssClassName);
    divBuilder.InnerHtml = messageSpan +unorderedList.ToString(TagRenderMode.Normal);

    if (formContext != null)
    {
        if(!htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
        {
            // client val summaries need an ID
            divBuilder.GenerateId("validationSummary");
            formContext.ValidationSummaryId =divBuilder.Attributes["id"];
            formContext.ReplaceValidationSummary = false;
        }
    }

    return newMvcHtmlString(divBuilder.ToString(TagRenderMode.Normal));
}

private static stringGetUserErrorMessageOrDefault(HttpContextBase httpContext,ModelError error)
{
    return string.IsNullOrEmpty(error.ErrorMessage) ?null : error.ErrorMessage;
}


No comments:

Post a Comment