Showing posts with label validation. Show all posts
Showing posts with label validation. Show all posts

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

Today I had to customize the standard @Html.ValidationSummary that comes with ASP.NET MVC. Searched for it as always using Google and found this question at StackOverflow with two promising answers. One of them presented a nice approach that is the creation of a custom partial view called ValidationSummary.cshtml that lives in the standard Views\Shared folder. Initially I was afraid of implementing this because as one of the commenters said it didn’t play ball with jQuery Validate. I decided to experiment and see what would happen in my case. Guess what? It didn’t play ball with jQuery Validate. So it’s not OK. I was not satisfied and did further investigation regarding Microsoft's jquery.validate.unobtrusive.js file and it led me to the correct implementation to get the custom Validation Summary working with jQuery Validate. Let’s look at the code that Microsoft uses to fill the Validation Summary with errors:

function onErrors(event, validator) {  // 'this' is the form element
    var container = $(this).find("[data-valmsg-summary=true]"),
        list = container.find("ul");

    if (list && list.length && validator.errorList.length) {
        list.empty();
        container.addClass("validation-summary-errors").removeClass("validation-summary-valid");

        $.each(validator.errorList, function () {
            $("<li />").html(this.message).appendTo(list);
        });
    }
}

As we can see the code looks for a container that has a data attribute [data-valmsg-summary=true]. So when creating our custom Validation Summary template we must add that data attribute to our <div> container like this:

@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">
        @Localization.CorrectErrors
    </div>
    <div class="panel-body">
        <ul>
            @foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
</div>

This Partial View approach gives you total control over what HTML structure/tags, CSS classes, etc you want applied to the validation summary.

Two things to take not in the above code:

1 - When the form is accessed for the 1st time, ModelState.IsValid is true and the CSS class validation-summary-valid is applied to the containing <div>.This is so so that it conforms to the standard ValidationSummary that comes with ASP.NET MVC and because this CSS class is used by jQuery unobtrusive validation code.
The standard Site.css file that comes with an empty ASP.NET MVC project also uses this very CSS class to hide the validation summary initially. It has this CSS rule:

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

When post backing the form data, the view Model may pass the unobtrusive client side validation but may not pass the server side validation and in this case ModelState.IsValid will be false. Again to be conformant with jquery validate unobtrusive code we must set the class validation-summary-errors to the validation summary container. This is imperative; if we do not do this, then we won’t see the validation errors that come from the server side since the validation summary will be hidden.

2 - panel panel-danger are Bootstrap’s CSS classes that give some charming to our custom ValidationSummary. You can check Bootstrap 3 panel component here.

Following the above simple HTML structure it’s possible to make the custom validation summary compatible with jQuery Validate.

Here’s the end result:

ASP.NET MVC Custom Validation Summary with Bootrap 3 Look & Feel using Panel component

To get the above look & feel I changed some CSS classes in ~/Content/Site.css file to override Bootstrap’s CSS. Here are them:

.panel-body {
    padding: 10px;
}

ul, ol
{
    margin-bottom: 0;
    padding-left: 17px;
}

.panel
{
    margin-bottom: 0;
}

.form-group {
    margin-bottom: 0;
}

As a final take on this matter, it’d be great if the standard ASP.NET MVC @Html.ValidationSummary had a method overload accepting a template name. Let’s hope that in the future this gets added.

I assembled a sample ASP.NET MVC project and uploaded it to GitHub. Feel free to play with it and adapt it to your use case. Here’s it is: https://github.com/leniel/AspNetMvcCustomHtmlValidationSummary

Happy coding! Party smile

Validating Date with MVC Foolproof Validation on the client side

This is a quick tip for something that I just tried with an ASP.NET MVC app and that worked as expected.

I use MVC Foolproof Validation to complement the standard validators that come with ASP.NET MVC in the System.ComponentModel.DataAnnotations assembly. Foolproof has some awesome validators like:

[Is]
[EqualTo]
[NotEqualTo]
[GreaterThan]
[LessThan]
[GreaterThanOrEqualTo]
[LessThanOrEqualTo]
[RequiredIf] [RequiredIfNot] [RequiredIfTrue] [RequiredIfFalse] [RequiredIfEmpty] [RequiredIfNotEmpty] [RequiredIfRegExMatch] [RequiredIfNotRegExMatch]

You can get it through a NuGet package here.

Let’s say you have a form with a DateTime field called “Show Until” that is part of a Notice object. This property stores a date value that should be in the future obviously. It makes no sense to set a date in the past for this field. So the question is: how can you get client side validation for this field without the need of going to the server to compare the date the user entered with today’s date?

This is what this post will show you…

This is the model property decorated with the respective data annotation GreaterThanOrEqualTo:

[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Localization))]
[Display(Name = "ShowUntil", ResourceType = typeof(Localization))]
[GreaterThanOrEqualTo("Today", ErrorMessageResourceName = "GreaterThanOrEqualTo", ErrorMessageResourceType = typeof(Localization))]
public DateTime ShowUntil { get; set; }

Pay attention to that "Today", string value. It tells Foolproof’s validator to compare the ShowUntil property with a property called Today (the dependent property). Wait a moment: the Notice object doesn’t have a property called Today since it’s not necessary. What’s the point? Confused smile

Now let’s add a partial class to extend the Notice class with a property called Today. This property returns Today’s date of course.

public partial class Notice
{
    public DateTime Today
    {
        get { return DateTime.Today; }
    }
}

Run the app now, enter a date in the past for the ShowUntil field and submit the form. You’ll see that a postback is made to the server and only after that the validation error message is shown to the user. Foolproof’s validator doesn’t know what’s the value of Today yet since it’s not part of the form data being edited. Remember: this kind of server side validation is not what we want. We want client side validation. We don’t want this data round-trip to the server just to validate one field in the form. So, how to solve this? It’s pretty simple…

Inside your form view (.cshtml file) since I’m using C# as the programming language and Razor view engine, add this hidden field:

@Html.HiddenFor(model => model.Today)

Now reload the page and try entering a date in the past like yesterday’s date. You’ll see that the user gets the validation error message instantly. Why? Because now Foolproof’s jQuery validator that is added to page has Today’s date value present in the form’s HTML output (that’s the purpose of the hidden field). If you look at the form/page source code in the browser (right click the browser window and select “View Page Source” in Firefox), you’ll se this value:

<input data-val="true" id="Today" name="Today" type="hidden" value="29/06/2012 00:00:00" />

That’s it. No date in the past is allowed when adding a new Notice to the system.

This use case can be considered a good use for Hidden fields. They serve a multitude of purposes…

Hope it helps.

Updated at 12:45 PM

You see how things are... after posting this I just discovered a really straightforward way of getting what I wanted (by chance) I can tell you. I wasn’t looking for this anymore when I hit this StackOverflow question. Looks like God took me there… Open-mouthed smile I happen to be using jQuery UI Datepicker control too. It has between its vast array of options a minDate property. So if you do this:

<script type="text/javascript">

    $(document).ready(function ()
    {
        $("#ShowUntil").datepicker('option', 'minDate', new Date());
    });

</script>

new Date() is Today in Javascript and so the datepicker won’t allow the selection of dates in the past. One word: AWESOME. One liner with jQuery help!