Perform client side validation for custom attribute
Asked Answered
D

2

76

I have created a Custom Validation Attribute:

public class FutureDateAttribute : ValidationAttribute
    {
        public override bool IsValid(object value) 
        {
            if (value == null|| (DateTime)value < DateTime.Now)
                return false;

            return true;
        }

    }

How can I get this to work on client side too with jquery?

Diatonic answered 20/1, 2011 at 12:36 Comment(0)
S
169

Here's how to proceed:

Start by defining the custom validation attribute:

public class FutureDateAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        if (value == null || (DateTime)value < DateTime.Now)
            return false;

        return true;
    }

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

Notice how it implements IClientValidatable. Next we write our model:

public class MyViewModel
{
    [FutureDate(ErrorMessage = "Should be in the future")]
    public DateTime Date { get; set; }
}

Then a controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel
        {
            // intentionally put in the past
            Date = DateTime.Now.AddDays(-1)
        });
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

and finally a view:

@using (Html.BeginForm())
{
    @Html.LabelFor(x => x.Date)
    @Html.TextBoxFor(x => x.Date)
    @Html.ValidationMessageFor(x => x.Date)
    <input type="submit" value="OK" />
}

The last part for the magic to happen is to define the custom adapter:

<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">
    // we add a custom jquery validation method
    jQuery.validator.addMethod('greaterThan', function (value, element, params) {
        if (!/Invalid|NaN/.test(new Date(value))) {
            return new Date(value) > new Date($(params).val());
        }
        return isNaN(value) && isNaN($(params).val()) || (parseFloat(value) > parseFloat($(params).val()));
    }, '');

    // and an unobtrusive adapter
    jQuery.validator.unobtrusive.adapters.add('futuredate', { }, function (options) {
        options.rules['greaterThan'] = true;
        options.messages['greaterThan'] = options.message;
    });
</script>
Squab answered 20/1, 2011 at 13:5 Comment(11)
@raklos, that will depend on the localization settings of the browser and the server. If you have differences things get complicated as one format could pass client validation but not server validation, and vice versa. Also it's up to you to decide what format you want your dates to be in.Squab
aaah i could kiss you for thisInterlaminate
great example. To get the client side work for me the adapter needed changing return new Date(value) > new Date($(params).val()); to return new Date(value) > new Date();. new Date($(params).val()) to new Date()Normative
I am having a problem getting the client side to work using Ajax.BeginForm instead of Html.BeginForm. Would there be an issue in this scenario?Rowboat
@user1790300, you must have forgotten to reference the jquery.unobtrusive-ajax.js script.Squab
js files: <script src="/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script> <script src="/Scripts/jquery-ui-1.9.1.custom.js" type="text/javascript"></script> <script type="text/javascript" src="/Scripts/kendo.web.min.js"></script> <script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script> <script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script> <script src="/Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script> <script src="/Scripts/conditional-validation.js" type="text/javascript"></script>Rowboat
@user1790300, then maybe the issue lies somewhere else in your code.Squab
The weird thing is that it is acting as if all of my RequiredIf are required and it is ignoring the conditions.Rowboat
@kehrk, I don't quite understand your question. You have to include those scripts in your view to get them working.Squab
@kehrk, yes if you are using ASP.NET 4 bundles it is best to make it part of some bundle.Squab
Will client side validation work without the custom jquery validation method? I'm trying to implement something like this but without all the datetime stuff and instead I just check for a bool.Nitroso
R
6

It took a little while since your question was asked, but if you still like metadata, and you're still open for simplified alternatives, you can solve your problem using following annotations:

[Required]
[AssertThat("Date > Now()")]
public DateTime? Date { get; set; }

It works for both - server and client, out of the box. For further details take a look at ExpressiveAnnotations library.

Rob answered 17/7, 2014 at 20:51 Comment(1)
You saved my day with your library. So simple in usage and very useful. ThanksUngracious

© 2022 - 2024 — McMap. All rights reserved.