68

I am working on a project with Asp.Net MVC3

In a View I have @Html.ValidationSummary(true) and as usually it produces

<div class="validation-summary-errors">
    <ul>
        <li>Something bad Happened!</li>
    </ul>
</div>

How can I extend this ValidationSummary to MyValidationSummary and produces the Html Code template something like this:

<div class="notification warning"> 
    <span></span> 
    <div class="text"> <p>Something bad Happened!</p> </div> 
</div>
user229044
  • 232,980
  • 40
  • 330
  • 338
Dynamikus
  • 2,861
  • 4
  • 22
  • 20
  • How can I get this to work with unobtrusive JQuery? – Bob Sep 15 '11 at 15:48
  • unobtrusive jQuery is for form validations on client side!!!, here what I want is how to display errors that happened on serverside to the client – Dynamikus Sep 16 '11 at 09:54

7 Answers7

145

My approach is to use a custom ValidationSummary.cshtml:

@model ModelStateDictionary

@if(!Model.IsValid)
{
    <div class="validation-summary-errors">
        <ul>
            @foreach (var modelError in 
                     Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
}

Put this partial view in your Shared folder and refer to it from your code:

@Html.Partial("_ValidationSummary", ViewData.ModelState);

This way you remain in full control of your html.

p.campbell
  • 98,673
  • 67
  • 256
  • 322
flo scheiwiller
  • 2,706
  • 2
  • 17
  • 15
  • 6
    A similar but imo better (no strings == better) approach is to create a display template for ModelStateDictionary, then the code becomes `@Html.DisplayFor(m => ViewData.ModelState)` – Jamie Ide Nov 28 '12 at 14:43
  • 1
    I was getting compile errors using the example above and had to do it this way in MVC3 Razor: `@{ Html.RenderPartial("ValidationSummary", ViewData.ModelState); }` – Flea Apr 06 '13 at 15:31
  • 3
    This approach won't work if you use jquery validation. At least I didn't get it to work. – Julian Jun 20 '13 at 16:59
  • 1
    @Julian : to make it work with jQuery Validate, take a look here: http://stackoverflow.com/a/18522431/114029 – Leniel Maccaferri Aug 29 '13 at 23:23
  • @LenielMacaferi, Thanks, I'll try this soon and come back to upvote you if this worked! – Julian Sep 12 '13 at 11:55
  • Compiler was not happy with RenderPartial. So, I used @Html.Partial("ValidationSummary", ViewData.ModelState); – piris Apr 04 '14 at 20:09
  • The OP used ValidationSummary with excludePropertyErrors=true. This solution will include property errors. – R. Schreurs Feb 13 '15 at 14:02
49

This question details the procedure of writing custom validation summary.

EDIT This will do what you want:

public static class LinqExt 
{
    public static string MyValidationSummary(this HtmlHelper helper, string validationMessage="")
    {
        string retVal = "";
        if (helper.ViewData.ModelState.IsValid)
            return "";

        retVal += "<div class='notification-warnings'><span>";
        if (!String.IsNullOrEmpty(validationMessage))
            retVal += helper.Encode(validationMessage);
        retVal += "</span>";
        retVal += "<div class='text'>";
        foreach (var key in helper.ViewData.ModelState.Keys) 
        {
            foreach(var err in helper.ViewData.ModelState[key].Errors)
                retVal += "<p>" + helper.Encode(err.ErrorMessage) + "</p>";
        }
        retVal += "</div></div>";
        return retVal.ToString();
    }
}

The code is self explanatory; just enumerating through modelstate errors and wrapping errors in dom element of your choice. There is an error that is if i use it like:

<%:Html.MyValidationSummary()%>

It will display html tags on the page as text rather than rendering it.

<%=Html.MyValidationSummary()%>

This works fine.

Community
  • 1
  • 1
Muhammad Adeel Zahid
  • 17,474
  • 14
  • 90
  • 155
26

Building upon flos's answer, I made it compatible with Microsoft's jQuery Unobtrusive Validation and added Bootstrap's 3 panel styling. Here's the new code:

@model ModelStateDictionary

<div class="@(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors") panel panel-danger"
     data-valmsg-summary="true">
    <div class="panel-heading">
        Please, correct the following errors:
    </div>
    <div class="panel-body">
        <ul>
            @foreach(var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
</div>

You can read about it in full detail here:

Creating a custom ASP.NET MVC @Html.ValidationSummary styled with Bootstrap 3 panel

I also created a sample ASP.NET MVC project to show this custom ValidationSummary in action. Get it here:

https://github.com/leniel/AspNetMvcCustomHtmlValidationSummary

Community
  • 1
  • 1
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
2

Just posting my answer here because it's working well for me ;)

I use a simple extension method that takes an MvcHtmlString and decodes it back to HTML:

    public static MvcHtmlString ToMvcHtmlString(this MvcHtmlString htmlString)
    {
        if (htmlString != null)
        {
            return new MvcHtmlString(HttpUtility.HtmlDecode(htmlString.ToString()));
        }
        return null;
    }

To plumb this in, I add the validation summary helper to my chstml like this:

@Html.ValidationSummary(true).ToMvcHtmlString()

This means, I can add custom HTML to my validation summaries:

ModelState.AddModelError("", "<p>This message can have html in it</p>");

And I can even add custom HTML to my field validation messages:

ModelState.AddModelError("MyField", "<p>This message can have html in it</p>");

And to get my field validation messages to work with HTML:

@Html.ValidationMessageFor(model => model.MyField).ToMvcHtmlString();
TotPeRo
  • 6,561
  • 4
  • 47
  • 60
Matt Roberts
  • 26,371
  • 31
  • 103
  • 180
  • To make this work, the code has to be put in a separate static class and then @using needs to be added to your View. Then it works PERFECTLY! Both client and server validation errors! Thank you! – z-boss Feb 26 '17 at 00:23
1

Adding related styles:

.field-validation-error {
    color: #b94a48;
}

.field-validation-valid {
    display: none;
}

input.input-validation-error {
    border: 1px solid #b94a48;
}

input[type="checkbox"].input-validation-error {
    border: 0 none;
}

.validation-summary-errors {
    color: #b94a48;
}

.validation-summary-valid {
    display: none;
}
Yovav
  • 2,557
  • 2
  • 32
  • 53
1

I've just had to do something similar for server side validation only ( e.g checking file contents) and have ended up completely usurping the @Html.ValidationSummary altogether with quite a nice little work around.

We have a BaseController class that extends Controller, and inside we override the OnActionExecuting method for several purposes. We create a new list in ViewBag for our error messages and ensure before any action runs it is initialized. Then we can add our errors to be displayed to it, and display on screen.

For the purposes of this question it would look like this.

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (ViewBag.ErrorsList == null)
        {
            ViewBag.ErrorsList = new List<string>();
        }
    }
}

Then in our _Layout.cshtml add the following above your @RenderBody()

@if(ViewBag.ErrorsList.Count > 0)
{
    <div class="container margin-top-10 alert alert-danger">
        <h3><i class="glyphicon glyphicon-warning-sign"></i></h3><br/>
        @foreach (string error in @ViewBag.ErrorsList)
        {
            @error <br/>
        }
    </div>
    @RenderBody()
}

Now whenever an error occurs server side that we want to display as a validation error message we simply add it to our ViewBag.ErrorsList

ViewBag.ErrorsList.Add("Something bad happened...");

And voila, one custom container for your server side validation error messages with any styles you want on it, with errors passed in the same manner as ValidationSummary.

Dean Puckett
  • 66
  • 10
0

I wanted to show just top-level message and nothing else. We already have validation next to the fields below. Working off @Leniel-Macaferi's solution this is what I did to make it work with jQuery validation: (added style="display: none;")

<div class="@(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors")"
     data-valmsg-summary="true">
    <div>
        There are still some fields not filled in before we can submit this. Please correct.
    </div>
    <div style="display: none;">
        <ul>
            @foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
</div>
Rob Koch
  • 1,523
  • 3
  • 18
  • 26