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?

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
raklos
  • 28,027
  • 60
  • 183
  • 301

2 Answers2

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>
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    @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. – Darin Dimitrov Jan 20 '11 at 15:18
  • aaah i could kiss you for this – Shashank Shekhar Dec 01 '11 at 14:34
  • 2
    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()** – PhilW Jan 26 '12 at 11:20
  • 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? – user1790300 Jul 29 '13 at 15:16
  • @user1790300, you must have forgotten to reference the `jquery.unobtrusive-ajax.js` script. – Darin Dimitrov Jul 29 '13 at 15:51
  • @user1790300, then maybe the issue lies somewhere else in your code. – Darin Dimitrov Jul 29 '13 at 16:09
  • The weird thing is that it is acting as if all of my RequiredIf are required and it is ignoring the conditions. – user1790300 Jul 29 '13 at 16:09
  • @kehrk, I don't quite understand your question. You have to include those scripts in your view to get them working. – Darin Dimitrov Aug 27 '13 at 16:17
  • 1
    @kehrk, yes if you are using ASP.NET 4 bundles it is best to make it part of some bundle. – Darin Dimitrov Aug 27 '13 at 16:25
  • 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. – Zollistic Jun 07 '16 at 15:56
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.

jwaliszko
  • 16,942
  • 22
  • 92
  • 158