Monday, July 27, 2015

Enabling ASP.Net MVC client validation for Kendo UI components

You might have stumbled upon this page because you are getting crazy in trying out all sort of solutions to make the client validation work for your Kendo components such as DropDownListForDatePickerForComboBoxFor, etc. You might have tried several suggestions you saw online like setting the validator defaults in your JavaScript like the samples below
$.validator.setDefaults({
    ignore: ""
});
or
   <script>
       $.validator.defaults.ignore = "";
   </script>
or
<script type="text/javascript">
    $(function () {
        var form = $('#yourFormName');
        form.data('validator').settings.ignore = ''// default is ":hidden".
    });
</script>
Some even offer not equating it to empty string but use an empty array
Do they all look similar?
Don't worry I tried them all as well but no luck and if you are using Unobtrusive validation I am not sure those solutions will not work.
Lets say you have a simple form like this.
01 Form
All you want to do is show that red outline when it’s validating like the one shown by the red arrows. But look at those other components shown by the blue arrows they are not validating.
02 Form
Whats the difference? they are all Kendo UI objects and since it’s a Kendo UI object we can use their validators so we don’t need to recreate whats already in there.
Lets just get some parts on my codes to properly demonstrate this.
First you need to properly set your DataAnnotation attributes. So for the viewModel in that example it will look like this
public class RegisterPageViewModel
{
    [DisplayName("First Name")]
    [Required]
    public string FirstName { getset; }

    [DisplayName("Last Name")]
    [Required]
    public string LastName { getset; }

    [DisplayName("Company Email")]
    [Required]
    public string CompanyEmail { getset; }

    [DisplayName("Date of Birth")]
    [Required]
    public DateTime? BirthDate { getset; }

    [DisplayName("First Secret Question")]
    [Required]
    public int SecretQuestion1Id { getset; }

    public IList<SelectListItem> SecretQuestion1IdList { getset; }

    [DisplayName("Answer to First Secret Question")]
    [Required]
    public string SecretQuestionAnswer1 { getset; }

    [DisplayName("Second Secret Question")]
    [Required]
    public int SecretQuestion2Id { getset; }

    public IList<SelectListItem> SecretQuestion2IdList { getset; }

    [DisplayName("Answer to Second Secret Question")]
    [Required]
    public string SecretQuestionAnswer2 { getset; }

    [Required]
    public int TrustedDomainId { getset; }

    public IList<SelectListItem> TrustedDomain { getset; }

}
Then on your view it looks like this
@model ExternalUserManagement.Web.Mvc.Controllers.ViewModels.Register.RegisterPageViewModel
@{
    ViewBag.Title = "Register";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="accountDetails" class="centre-container">
    @using (Ajax.BeginForm("CreateAccount""Register",
        new AjaxOptions
        {
            UpdateTargetId = "accountDetails",
            OnBegin = "windowHelper.displayWaitingDialog('Saving Registration Details, please wait...')",
            OnComplete = "windowHelper.close()"
        }))
    {  
        <p class="message information">Register you details, then click submit.</p>

        <div class="row">
            @Html.LabelFor(m => m.FirstName, new { @class = "label" })
            @Html.TextBoxFor(m => m.FirstName, new { @class = "input k-textbox" })
        </div>
        <div class="row">
            @Html.LabelFor(m => m.LastName, new { @class = "label" })
            @Html.TextBoxFor(m => m.LastName, new { @class = "input k-textbox" })

        </div>
        <div class="row">
            @Html.LabelFor(m => m.CompanyEmail, new { @class = "label" })
            @Html.TextBoxFor(m => m.CompanyEmail, new { @class = "input-left k-textbox" })
            
            @(Html.Kendo().DropDownListFor(m => m.TrustedDomainId)
                      .DataTextField("Text")
                      .DataValueField("Value")
                      .BindTo(Model.TrustedDomain)
                      .OptionLabel(" -- Please Select --")
                      .HtmlAttributes(new { @class = "input-right" })
                      )
        </div>
        <div class="row">
            @Html.LabelFor(m => m.BirthDate, new { @class = "label" })
            @Html.Kendo().DatePickerFor(m => m.BirthDate).HtmlAttributes(new { @class = "input" })
        </div>
        <div class="row">
            @Html.LabelFor(m => m.SecretQuestion1Id, new { @class = "label" })
            @(Html.Kendo().DropDownListFor(m => m.SecretQuestion1Id)
                      .DataTextField("Text")
                      .DataValueField("Value")
                      .BindTo(Model.SecretQuestion1IdList)
                      .OptionLabel(" -- Please Select --")
                      .HtmlAttributes(new { @class = "input" })
                      )
        </div>
        <div class="row">
            @Html.LabelFor(m => m.SecretQuestionAnswer1, new { @class = "label" })
            @Html.TextBoxFor(m => m.SecretQuestionAnswer1, new { @class = "input k-textbox" })
        </div>
        <div class="row">
            @Html.LabelFor(m => m.SecretQuestion2Id, new { @class = "label" })
            @(Html.Kendo().DropDownListFor(m => m.SecretQuestion2Id)
                      .DataTextField("Text")
                      .DataValueField("Value")
                      .BindTo(Model.SecretQuestion2IdList)
                      .OptionLabel(" -- Please Select --")
                      .HtmlAttributes(new { @class = "input" }).AutoBind(true)
                      )
        </div>
        <div class="row">
            @Html.LabelFor(m => m.SecretQuestionAnswer2, new { @class = "label" })
            @Html.TextBoxFor(m => m.SecretQuestionAnswer2, new { @class = "input k-textbox" })
        </div>
        <div class="captcha row">
            @Html.Label("Are you a human?"new { @class = "label" })
            <br />
            @Html.Raw(Html.GenerateCaptcha("captcha""clean"))
            @Html.ValidationMessage("Invalid Characters")
        </div>
        <div class="row">
            <div class="commands">
                <button class="k-button" type="submit" title="Sumbit">
                    <img src="@Url.Content("~/Content/Images/Icons/disk.png")" alt="" />
                    Sumbit
                </button>
            </div>
        </div>
    }
</div>
So if you’re not using Kendo UI components it would work normally but you are, that’s why you are reading this.  So the solution is simple.
Just add this
<script>
    $(function () {
        $("form").kendoValidator();
    });
</script>
and it would all start to magically validate like this.  Anyways if you found any other solution other than this, I guess you need to change it as this is directly using Kendo’s validation functions and is documented here.
03 Working
You do not even need a ValidationMessageFor.  But if you really need to you can add them like this without any side effects :)
@(Html.Kendo().DropDownListFor(m => m.TrustedDomainId)
            .DataTextField("Text")
            .DataValueField("Value")
            .BindTo(Model.TrustedDomain)
            .OptionLabel(" -- Please Select --")
            .HtmlAttributes(new { @class = "input-right" })
            )
@Html.ValidationMessageFor(m => m.TrustedDomainId)

No comments: