Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP .Net MVC 3 unobtrusive custom client validation

I have used the jquery validation plug in for a couple years now but this is my first effort to mixing with MVC 3 unobtrusive validation.

Information on the client side of things is scattered all over the internet and it's hard to find any thing that is deep enough to explain it to people that have not used it yet. I have been burning up Google for an hour now for an example on how to create a custom client side validator.

@Html.TextBoxFor(model => Model.CCPayment.CardNumber, new { @class = "textInput validateCreditCard", maxLength = "20" })

$(document).ready(function () {
      jQuery.validator.unobtrusive.adapters.add('validateCreditCard', {}, function (value, element) {
        alert('foo');
    });
});

if I run the above code in the bottom of my view it does absolutely nothing. I have even tried jquery.validator.addmethod() and still nothing. All the client side validation that is emitted from my model validation annotations works fine.

<div class="ctrlHolder">
                <label>
                    <em>*</em>
                    Card Number:
                </label>
                @Html.TextBoxFor(model => Model.CCPayment.CardNumber, new { @class = "textInput validateCreditCard", maxLength = "20" })

                <p class="field-validation-valid formHint" data-valmsg-for="CCPayment.CardNumber"></p>
             </div>
like image 403
JBeckton Avatar asked Jan 28 '11 21:01

JBeckton


People also ask

How can use client-side validation in ASP.NET MVC?

Firstly, you just need to create an ASP.NET MVC application. To create a new ASP.NET MVC application, Open Visual Studio choose File, New, then Project. It will open a New Project window, from where you need to choose node Visual C# and then Web and from the right pane you need to choose ASP.NET Web Application.

What is unobtrusive JavaScript validation in MVC?

An unobtrusive validation in jQuery is a set of ASP.Net MVC HTML helper extensions.By using jQuery Validation data attributes along with HTML 5 data attributes, you can perform validation to the client-side.


2 Answers

Here's how you could proceed. First you need to write a custom validator attribute in order to ensure that the validation is enforced on the server side. You could use the one described in this blog post:

public class CreditCardAttribute : ValidationAttribute, IClientValidatable
{
    private CardType _cardTypes;
    public CardType AcceptedCardTypes
    {
        get { return _cardTypes; }
        set { _cardTypes = value; }
    }

    public CreditCardAttribute()
    {
        _cardTypes = CardType.All;
    }

    public CreditCardAttribute(CardType AcceptedCardTypes)
    {
        _cardTypes = AcceptedCardTypes;
    }

    public override bool IsValid(object value)
    {
        var number = Convert.ToString(value);

        if (String.IsNullOrEmpty(number))
            return true;

        return IsValidType(number, _cardTypes) && IsValidNumber(number);
    }

    public override string FormatErrorMessage(string name)
    {
        return "The " + name + " field contains an invalid credit card number.";
    }

    public enum CardType
    {
        Unknown = 1,
        Visa = 2,
        MasterCard = 4,
        Amex = 8,
        Diners = 16,

        All = CardType.Visa | CardType.MasterCard | CardType.Amex | CardType.Diners,
        AllOrUnknown = CardType.Unknown | CardType.Visa | CardType.MasterCard | CardType.Amex | CardType.Diners
    }

    private bool IsValidType(string cardNumber, CardType cardType)
    {
        // Visa
        if (Regex.IsMatch(cardNumber, "^(4)")
            && ((cardType & CardType.Visa) != 0))
            return cardNumber.Length == 13 || cardNumber.Length == 16;

        // MasterCard
        if (Regex.IsMatch(cardNumber, "^(51|52|53|54|55)")
            && ((cardType & CardType.MasterCard) != 0))
            return cardNumber.Length == 16;

        // Amex
        if (Regex.IsMatch(cardNumber, "^(34|37)")
            && ((cardType & CardType.Amex) != 0))
            return cardNumber.Length == 15;

        // Diners
        if (Regex.IsMatch(cardNumber, "^(300|301|302|303|304|305|36|38)")
            && ((cardType & CardType.Diners) != 0))
            return cardNumber.Length == 14;

        //Unknown
        if ((cardType & CardType.Unknown) != 0)
            return true;

        return false;
    }

    private bool IsValidNumber(string number)
    {
        int[] DELTAS = new int[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 0 };
        int checksum = 0;
        char[] chars = number.ToCharArray();
        for (int i = chars.Length - 1; i > -1; i--)
        {
            int j = ((int)chars[i]) - 48;
            checksum += j;
            if (((i - chars.Length) % 2) == 0)
                checksum += DELTAS[j];
        }

        return ((checksum % 10) == 0);
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = this.ErrorMessage,
            ValidationType = "creditcard"
        };
    }
}

Notice that I've modified it to make it implement the IClientValidatable interface and added the GetClientValidationRules method which simply uses the same error message for client validation as the server side and provides an unique name for this validator that will be used by the jquery unobtrusive adapter. Now all that's left is to decorate your model property with this attribute:

[CreditCard(ErrorMessage = "Please enter a valid credit card number")]
[Required]
public string CardNumber { get; set; }

and in your view:

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        jQuery.validator.unobtrusive.adapters.addBool('creditcard');
    });
</script>

@using (Html.BeginForm())
{
    @Html.TextBoxFor(x => x.CardNumber)
    @Html.ValidationMessageFor(x => x.CardNumber)
    <input type="submit" value="OK" />
}
like image 105
Darin Dimitrov Avatar answered Nov 01 '22 20:11

Darin Dimitrov


You need ValidateFor in addition to TextBoxFor. I can't tell from your question if you've already done that or not. You also need EnableClientValidation before the form.

like image 42
Craig Stuntz Avatar answered Nov 01 '22 19:11

Craig Stuntz