Custom ValidationSummary template Asp.net MVC 3
Asked Answered
O

7

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>
Ottilie answered 2/5, 2011 at 13:39 Comment(2)
How can I get this to work with unobtrusive JQuery?Hussar
unobtrusive jQuery is for form validations on client side!!!, here what I want is how to display errors that happened on serverside to the clientOttilie
U
50

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.

Unseam answered 2/5, 2011 at 13:46 Comment(4)
Added helper.Encode to the messages, got to make sure and encode output to prevent injection.Talkingto
Also wrap return string in a MvcHtmlString and make return type MvcHtmlStringKirov
Is public access modifier missing in the definition of MyValidationSummary()?Dewan
I couldn't get it to work with out the public access modifierMosenthal
C
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.

Cultivator answered 16/12, 2011 at 12:43 Comment(7)
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)Raspings
I was getting compile errors using the example above and had to do it this way in MVC3 Razor: @{ Html.RenderPartial("ValidationSummary", ViewData.ModelState); }Dunfermline
This approach won't work if you use jquery validation. At least I didn't get it to work.Opia
@Opia : to make it work with jQuery Validate, take a look here: https://mcmap.net/q/280169/-custom-validationsummary-template-asp-net-mvc-3Womack
@LenielMacaferi, Thanks, I'll try this soon and come back to upvote you if this worked!Opia
Compiler was not happy with RenderPartial. So, I used @Html.Partial("ValidationSummary", ViewData.ModelState);Vulcanology
The OP used ValidationSummary with excludePropertyErrors=true. This solution will include property errors.Amphidiploid
U
50

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.

Unseam answered 2/5, 2011 at 13:46 Comment(4)
Added helper.Encode to the messages, got to make sure and encode output to prevent injection.Talkingto
Also wrap return string in a MvcHtmlString and make return type MvcHtmlStringKirov
Is public access modifier missing in the definition of MyValidationSummary()?Dewan
I couldn't get it to work with out the public access modifierMosenthal
W
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

Womack answered 29/8, 2013 at 23:15 Comment(1)
I had to add this to my site.css for it to work in my project: .validation-summary-valid { display: none; }Despinadespise
R
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();
Rissole answered 12/3, 2014 at 12:51 Comment(1)
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!Hausner
S
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;
}
Superload answered 16/6, 2015 at 6:36 Comment(0)
P
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.

Percuss answered 5/11, 2015 at 11:48 Comment(0)
D
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>
Dystopia answered 29/5, 2014 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.