Skip to content

Instantly share code, notes, and snippets.

@meataxe
Last active August 29, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meataxe/10603564 to your computer and use it in GitHub Desktop.
Save meataxe/10603564 to your computer and use it in GitHub Desktop.
Updated version of @Html.ValidationSummary to add the validation message source to the list items
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
// Use in views by calling `@Html.CunningValidationSummary(...)` instead of `@Html.ValidationSummary(...)`
// It will give an attribute of `data-valmsg-source` on each list item in the validation summary with a value of the prop the error relates to.
// Then you can use js to scroll to the field in question.
//
// eg.
// <div data-valmsg-summary="true" class="validation-summary-errors">
// <ul>
// <li data-valmsg-source="FundingSources">The funding sources must be $100,000</li>
// </ul>
// </div>
//
// See also: https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Mvc/Html/ValidationExtensions.cs
public static class CunningValidationSummaryHelper
{
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper)
{
return htmlHelper.CunningValidationSummary(false);
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors)
{
return htmlHelper.CunningValidationSummary(excludePropertyErrors, null);
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, string message)
{
return htmlHelper.CunningValidationSummary(false, message, null);
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message)
{
return htmlHelper.CunningValidationSummary(excludePropertyErrors, message, null);
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, string message, object htmlAttributes)
{
return htmlHelper.CunningValidationSummary(false, message, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message, object htmlAttributes)
{
return htmlHelper.CunningValidationSummary(excludePropertyErrors, message, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes)
{
return htmlHelper.CunningValidationSummary(false, message, htmlAttributes);
}
public static MvcHtmlString CunningValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message, IDictionary<string, object> htmlAttributes)
{
if (htmlHelper == null)
{
throw new ArgumentNullException("htmlHelper");
}
var formContextForClientValidation = GetFormContextForClientValidation(htmlHelper.ViewContext);
if (htmlHelper.ViewData.ModelState.IsValid)
{
if (formContextForClientValidation == null)
{
return null;
}
if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled && excludePropertyErrors)
{
return null;
}
}
string str;
if (!string.IsNullOrEmpty(message))
{
var tagBuilder = new TagBuilder("span");
tagBuilder.SetInnerText(message);
str = tagBuilder.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else
{
str = null;
}
var stringBuilder = new StringBuilder();
var tagBuilder2 = new TagBuilder("ul");
Dictionary<string, ModelState> enumerable = null; // This is probably a shit way to get the name
if (excludePropertyErrors)
{
// Not sure if we care about adding `data-valmsg-source` in this case?
ModelState modelState;
htmlHelper.ViewData.ModelState.TryGetValue(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix, out modelState);
if (modelState != null)
{
enumerable = new Dictionary<string, ModelState> { { string.Empty, modelState } };
}
}
else
{
if (htmlHelper.ViewData.ModelState != null && htmlHelper.ViewData.ModelState.Any())
{
enumerable = htmlHelper.ViewData.ModelState.ToDictionary(modelState => modelState.Key, modelState => modelState.Value);
}
}
if (enumerable != null)
{
// `current` is a field on the submitted form, we want to get its id or name attribute.
foreach (var current in enumerable)
{
// `current2` is an error associated with a field on the form
foreach (var current2 in current.Value.Errors)
{
var userErrorMessageOrDefault = GetUserErrorMessageOrDefault(current2);
if (!string.IsNullOrEmpty(userErrorMessageOrDefault))
{
var tagBuilder3 = new TagBuilder("li");
var nameOrId = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(current.Key);
tagBuilder3.MergeAttribute("data-valmsg-source", nameOrId);
tagBuilder3.SetInnerText(userErrorMessageOrDefault);
stringBuilder.AppendLine(tagBuilder3.ToString(TagRenderMode.Normal));
}
}
}
}
if (stringBuilder.Length == 0)
{
stringBuilder.AppendLine("<li style=\"display:none\"></li>");
}
tagBuilder2.InnerHtml = stringBuilder.ToString();
var tagBuilder4 = new TagBuilder("div");
tagBuilder4.MergeAttributes(htmlAttributes);
tagBuilder4.AddCssClass(htmlHelper.ViewData.ModelState.IsValid ? HtmlHelper.ValidationSummaryValidCssClassName : HtmlHelper.ValidationSummaryCssClassName);
tagBuilder4.InnerHtml = str + tagBuilder2.ToString(TagRenderMode.Normal);
if (formContextForClientValidation != null)
{
if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
{
if (!excludePropertyErrors)
{
tagBuilder4.MergeAttribute("data-valmsg-summary", "true");
}
}
else
{
tagBuilder4.GenerateId("validationSummary");
formContextForClientValidation.ValidationSummaryId = tagBuilder4.Attributes["id"];
formContextForClientValidation.ReplaceValidationSummary = !excludePropertyErrors;
}
}
return new MvcHtmlString(tagBuilder4.ToString(TagRenderMode.Normal));
}
private static FormContext GetFormContextForClientValidation(ViewContext context)
{
return !context.ClientValidationEnabled ? null : context.FormContext;
}
private static string GetUserErrorMessageOrDefault(ModelError error)
{
return !string.IsNullOrEmpty(error.ErrorMessage) ? error.ErrorMessage : null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment