Reputation: 4909
I am having a slight issue with the use of ValidationSummary(true)
to display model level errors. If the ModelState does not contain model errors (i.e. ModelState.AddModelError("", "Error Description")
) but contains property errors (added using data annotations) it displays the validation summary with no error information (when you view the source). My css is therefore displaying an empty red box like so:
If there are no property errors then no validation summary is displayed. With ValidationSummary(true)
I would expect it to only display validation errors if there are model errors. What have I misunderstood?
I have a basic project as follows:
Controller:
public class HomeController : Controller
{
public ViewResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
return View();
}
}
Model:
public class IndexViewModel
{
[Required]
public string Name { get; set; }
}
View:
@model IndexViewModel
@Html.ValidationSummary(true)
@using(@Html.BeginForm())
{
@Html.TextBoxFor(m => m.Name)
<input type="submit" value="submit" />
}
Upvotes: 37
Views: 30875
Reputation: 28611
Update for .net core Here's a .net core equivalent for VJAI's answer:
[HtmlTargetElement("div", Attributes = "asp-errors-only-validation-summary")]
public class ErrorsOnlyValidationSummaryTagHelper : ValidationSummaryTagHelper
{
public ErrorsOnlyValidationSummaryTagHelper(IHtmlGenerator generator)
: base(generator)
{
base.ValidationSummary = ValidationSummary.All;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
if (this.ValidationSummary == ValidationSummary.None)
return;
TagBuilder validationSummary = this.Generator.GenerateValidationSummary(this.ViewContext, this.ValidationSummary == ValidationSummary.ModelOnly, (string)null, (string)null, (object)null);
if (validationSummary == null)
{
output.SuppressOutput();
}
else
{
if (!validationSummary.HasInnerHtml)
return;
var xel = XElement.Parse(ToHtmlString(validationSummary));
var lis = xel.Element("ul")?.Elements("li");
if (lis != null && lis.Count() == 1 && lis.First().Value == string.Empty)
return;
output.MergeAttributes(validationSummary);
output.PostContent.AppendHtml((IHtmlContent)validationSummary.InnerHtml);
}
}
public string ToHtmlString(IHtmlContent content)
{
if (content is HtmlString htmlString)
{
return htmlString.Value ?? string.Empty;
}
using (var writer = new StringWriter())
{
content.WriteTo(writer, HtmlEncoder.Default);
return writer.ToString();
}
}
}
Usage:
<div asp-errors-only-validation-summary></div>
Upvotes: 0
Reputation: 32758
I think there is something wrong with the ValidationSummary
helper method. You could easily create a custom helper method that wraps the built-in ValidationSummary
.
public static MvcHtmlString CustomValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors)
{
var htmlString = htmlHelper.ValidationSummary(excludePropertyErrors);
if (htmlString != null)
{
XElement xEl = XElement.Parse(htmlString.ToHtmlString());
var lis = xEl.Element("ul").Elements("li");
if (lis.Count() == 1 && lis.First().Value == "")
return null;
}
return htmlString;
}
Then from your view,
@Html.CustomValidationSummary(true)
Upvotes: 29
Reputation: 2440
I know this topic is old, but the behavior still exists even in MVC 5. It's definitely not the behavior most of us expect. When we want a "non-property" ModelState error, we do this:
ModelState.AddModelError(string.Empty, ex.Message);
Since we only want to display the summary if the empty key exists, this HtmlHelper does the trick and is cleaner than the accepted answer IMO:
public static MvcHtmlString CustomValidationSummary(this HtmlHelper htmlHelper,
bool excludePropertyErrors, string message = null)
{
// Don't render summary if there are no empty keys when excluding property errors
if (excludePropertyErrors && !htmlHelper.ViewData.ModelState.ContainsKey(string.Empty))
return null;
// Use default
return htmlHelper.ValidationSummary(excludePropertyErrors, message);
}
When excluding property errors, it's likely that you don't need the summary DIV available for client-side validation, so not rendering it at all is fine. I hope this helps some of you.
Upvotes: 0
Reputation: 2773
ValidationSummary accepts an optional message parameter. If you set this parameter, then the box doesn't look as silly.
@Html.ValidationSummary(true, "Sorry, that didn't work. Please check the details submitted and try again.")
Upvotes: 1
Reputation: 12739
I was having this empty validation summary issue. I just set excludePropertyErrors
to false - and it added the errors to the validation summary.
@Html.ValidationSummary(false)
I realise this isn't necessarily what is being asked here - although this does solve the empty validation summary issue - and is an option if you're having this problem.
Upvotes: 1
Reputation: 173
@if (ViewContext.ViewData.ModelState.Count > 0)
{
//Your content
}
Would work like charm.
Upvotes: 1
Reputation: 658
It's the validation scripts doing this.
Change the following the web.config
<add key="ClientValidationEnabled" value="false" />
It should be false
Upvotes: -1
Reputation: 1205
Another variation of the fix with Bootstrap classes is:
public static class ValidationSummaryExtensions
{
public static MvcHtmlString CleanValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message = null)
{
if(htmlHelper == null) throw new ArgumentNullException("htmlHelper");
MvcHtmlString validationSummary = null;
if (htmlHelper.ViewData.ModelState.ContainsKey(string.Empty))
{
var htmlAttributes = new { @class = "alert alert-danger" };
validationSummary = htmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes);
}
return validationSummary;
}
}
Upvotes: 1
Reputation: 1061
@if (ViewContext.ViewData.ModelState.Where(x => x.Key == "").Any())
{
@Html.ValidationSummary(true, null, new { @class = "ui-state-error" })
}
This checks if there are any model wide errors and only renders the summary when there are some.
Upvotes: 37
Reputation: 23289
Check this question too.
You could hide the summary with CSS:
.validation-summary-valid { display:none; }
Also, you could put the validation summary before the Html.BeginForm()
.
Upvotes: 22
Reputation: 89527
Another way of doing it is to check if there are any li elements, if not just hide validation-summary-errors
<script type="text/javascript">
$(document).ready(function () {
if ($(".validation-summary-errors li:visible").length === 0) {
$(".validation-summary-errors").hide();
}
});
</script>
Upvotes: 7
Reputation: 21
I know a workaround has been found but I had a similar problem with an easier solution.
Is your validation summary rendering with css class "validation-summary-valid" or "validation-summary-errors"? The first class is applied with the validation summary is empty, the second is applied when it's populated.
I've noticed that a placeholder div is rendered if the validation summary contains no errors so client side validation can display it if any fields fail validation.
In the default MVC stylesheet 'Site.css' they just suppress displaying the empty validation summary with 'display:none;' e.g. .validation-summary-valid { display: none; }
Upvotes: 2