Has anyone seen an MVC3 data annotation for Date validation that requires a single selected date to be equal to or greater than current date?
If there's already a third party add on that's cool too. I'm already using the DataAnnotationsExtensions but it doesn't offer what I'm looking for.
There doesn't seem to be any reference of this on. So, hoping someone has already solved this before I try to reinvent the wheel and write my own custom validator.
I've tried Range but that requires 2 dates and both have to be constants in string format such as [Range(typeof(DateTime), "1/1/2011", "1/1/2016")] but that doesn't help. And the DataAnnotationsExtensions Min validator only accepts int and double
Update Solved
Thanks to #BuildStarted this is what I ended up with and it works great server-side and now client side with my script
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Web.Models.Validation {
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class DateMustBeEqualOrGreaterThanCurrentDateValidation : ValidationAttribute, IClientValidatable {
private const string DefaultErrorMessage = "Date selected {0} must be on or after today";
public DateMustBeEqualOrGreaterThanCurrentDateValidation()
: base(DefaultErrorMessage) {
}
public override string FormatErrorMessage(string name) {
return string.Format(DefaultErrorMessage, name);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
var dateEntered = (DateTime)value;
if (dateEntered < DateTime.Today) {
var message = FormatErrorMessage(dateEntered.ToShortDateString());
return new ValidationResult(message);
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
var rule = new ModelClientCustomDateValidationRule(FormatErrorMessage(metadata.DisplayName));
yield return rule;
}
}
public sealed class ModelClientCustomDateValidationRule : ModelClientValidationRule {
public ModelClientCustomDateValidationRule(string errorMessage) {
ErrorMessage = errorMessage;
ValidationType = "datemustbeequalorgreaterthancurrentdate";
}
}
}
And in my model
[Required]
[DateMustBeEqualOrGreaterThanCurrentDate]
public DateTime SomeDate { get; set; }
The client side script
/// <reference path="jquery-1.7.2.js" />
jQuery.validator.addMethod("datemustbeequalorgreaterthancurrentdate", function (value, element, param) {
var someDate = $("#SomeDate").val();
var today;
var currentDate = new Date();
var year = currentDate.getYear();
var month = currentDate.getMonth() + 1; // added +1 because javascript counts month from 0
var day = currentDate.getDate();
var hours = currentDate.getHours();
var minutes = currentDate.getMinutes();
var seconds = currentDate.getSeconds();
today = month + '/' + day + '/' + year + ' ' + hours + '.' + minutes + '.' + seconds;
if (someDate < today) {
return false;
}
return true;
});
jQuery.validator.unobtrusive.adapters.addBool("datemustbeequalorgreaterthancurrentdate");
Create a custom attribute.
public class CheckDateRangeAttribute: ValidationAttribute {
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
DateTime dt = (DateTime)value;
if (dt >= DateTime.UtcNow) {
return ValidationResult.Success;
}
return new ValidationResult(ErrorMessage ?? "Make sure your date is >= than today");
}
}
code was written off the cuff so fix any errors :)
Use [Remote] for special validations, simple and easy:
Your model:
[Remote("ValidateDateEqualOrGreater", HttpMethod="Post",
ErrorMessage = "Date isn't equal or greater than current date.")]
public DateTime Date { get; set; }
//other properties
Your action:
[HttpPost]
public ActionResult ValidateDateEqualOrGreater(DateTime Date)
{
// validate your date here and return True if validated
if(Date >= DateTime.Now)
{
return Json(true);
}
return Json(false);
}
Simple way to accomplish this task is using CompareValidator.
The code below uses AjaxControlToolKit's CalendarExtender.
Copy the below code to your HTML directive
<asp:TextBox ID="txtCompletionDate" runat="server" CssClass="txtNormal"></asp:TextBox>
<cc1:CalendarExtender ID="CalendarExtender1" TargetControlID="txtCompletionDate"
Format="dd/MM/yyyy" runat="server">
</cc1:CalendarExtender>
<asp:RequiredFieldValidator ID="rfvCompletionDate" runat="server" ControlToValidate="txtCompletionDate"
CssClass="labelError" ErrorMessage="*"></asp:RequiredFieldValidator>
<asp:CompareValidator ID="cvDate" runat="server" ControlToCompare="hiddenbox" ErrorMessage="*"
ControlToValidate="txtCompletionDate" CssClass="labelError" ToolTip="Completion Date should be greater than or equal to Today"
Operator="GreaterThanEqual" Type="Date"></asp:CompareValidator>
<asp:TextBox ID="hiddenbox" runat="server" CssClass="hiddenbox">
</asp:TextBox>
add the below line in the CSS
.hiddenbox {display:none;}
Related
Edit
I found that the problem is that View Components are unable to have an #section (see ViewComponent and #Section #2910 ) so adding custom client-side validation using the unobtrusive library seems imposible (or very complex). Moreover, the inability of including the required javascript into a View Component makes me regret of following this approach to modularize my app in the first place...
I am learning to make custom validation attributes with client-side support. I was able to implement a custom validator for a string property and it works pretty well, but when I tried to make one for input file it doesn't work (i.e. when I select a file in my computer, the application doesn't display the validation messages. The server-side validation works. Here is some code that shows my implementation.
The class of the model
public class UploadPanelModel
{
public int? ID { get; set; }
public string Title { get; set; }
public string Description { get; set; } //Raw HTML with the panel description
[FileType(type: "application/pdf")]
[FileSize(maxSize: 5000000)]
public IFormFile File { get; set; }
public byte[] FileBytes { get; set; }
public ModalModel Modal { get; set; } //Only used if the Upload panel uses a modal.
The validator
public class FileSizeAttribute : ValidationAttribute, IClientModelValidator
{
private long _MaxSize { get; set; }
public FileSizeAttribute (long maxSize)
{
_MaxSize = maxSize;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
UploadPanelModel panel = (UploadPanelModel)validationContext.ObjectInstance;
return (panel.File==null || panel.File.Length <= _MaxSize) ? ValidationResult.Success : new ValidationResult(GetFileSizeErrorMessage(_MaxSize));
}
private string GetFileSizeErrorMessage(long maxSize)
{
double megabytes = maxSize / 1000000.0;
return $"El archivo debe pesar menos de {megabytes}MB";
}
public void AddValidation(ClientModelValidationContext context)
{
if(context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-filesize", GetFileSizeErrorMessage(_MaxSize));
var maxSize = _MaxSize.ToString();
MergeAttribute(context.Attributes, "data-val-filesize-maxsize", maxSize);
}
private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
The javascript in the Razor View
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$.validator.addMethod('filesize',
function (value, element, params) {
var size = $((params[0]).val()).size(),
maxSize = params[1];
if (size < maxSize) {
return false;
}
else {
return false;
}
}
);
$.validator.unobtrusive.adapters.add('filesize',
['maxSize'],
function (options) {
var element = $(options.form).find('input#File')[0];
options.rules['filesize'] = [element, options.params['maxSize']];
options.messages['filesize'] = options.message;
}
);
</script>
I always return false in the javascript method to force the application to show the validation error regardless the chosen file, but it still doesn't work.
Your addMethod() function will be throwing an error because params[0] is not a jQuery object and has no .val() (you also have the $ in the wrong place). You would need to use
var size = params[0].files[0].size;
However I suggest you write you scripts as
$.validator.unobtrusive.adapters.add('filesize', ['maxsize'], function (options) {
options.rules['filesize'] = { maxsize: options.params.maxsize };
if (options.message) {
options.messages['filesize'] = options.message;
}
});
$.validator.addMethod("filesize", function (value, element, param) {
if (value === "") {
return true;
}
var maxsize = parseInt(param.maxsize);
if (element.files != undefined && element.files[0] != undefined && element.files[0].size != undefined) {
var filesize = parseInt(element.files[0].size);
return filesize <= maxsize ;
}
return true; // in case browser does not support HTML5 file API
});
I have following code in custom validation attribute called DateRange:
private DateTime _minDate = DateTime.Today.AddYears(-100);
private DateTime _maxDate = DateTime.MaxValue;
// String representation of the Min Date (yyyy/MM/dd)
public string Min
{
get { return FormatDate(_minDate, DateTime.Today.AddYears(-100)); }
set { _minDate = value == "Today" ? DateTime.Today : ParseDate(value, DateTime.Today.AddYears(-100)); }
}
// String representation of the Max Date (yyyy/MM/dd)
public string Max
{
get { return FormatDate(_maxDate, DateTime.MaxValue); }
set { _maxDate = value == "Today" ? DateTime.Today : ParseDate(value, DateTime.MaxValue); }
}
Then I write this attribute in metadata on some property of entity model like this:
[DateRange(Max = "Today")]
public string SomeDateProperty { get; set; };
I set breakpoint on Max property's getter. First time I open view, breakpoint is activated and DateTime.Today is got. Consequent refresh of the view does not activate breakpoint and old value is got. I think it's caching validation attribute. My question is: Is this because of caching? If it is, then how to disable it? Thanks in advance
The constructor for the custom attributes only get hit once, no idea how to turn off any sort of caching. The way I got round this for my scenario, was to only deal with the date calculation in the "IsValid" Method.
I created a date in the past attribute, that needed the date to be in the past, but you could set how long in the past was valid.
public class DateInPastAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "'{0}' must be in the past.";
public int DaysInPastAllowed { get; set; }
public DateInPastAttribute(int daysInPastAllowed)
: base(DefaultErrorMessage)
{
this.DaysInPastAllowed = daysInPastAllowed;
}
public override bool IsValid(object value)
{
if (!(value is DateTime))
{
return true;
}
DateTime maxDate = DateTime.Now;
DateTime minDate = maxDate.AddDays(this.DaysInPastAllowed * -1);
DateTime dateValue = (DateTime)value;
return
minDate <= dateValue &&
dateValue <= maxDate;
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, this.ErrorMessageString, name);
}
}
You can then use it in your view model like this:
[DateInPast(365)]
public DateTime DateReceived { get; set; }
Which would allow a date to be entered within the last year. You could amend this for the scenario that you require.
What do I want to achieve:
To show a seperate validation message for failing minimum age check and one for maximum age check
To store the minimum and maximum age in one area as integers. Not in js/ validator... only in the model. which I hope to change to look at a config file.
For the validation to work with jquery unobtrusive and server side, and to be in one place against the model (and obv some jquery)
To be enabled using data annotations
I wanted to check against DOB as a datetime, not have the user put in there age as an int. If i did I could have used [Min] notation and [Max] notation for age. It wasn't good enough.
Why didn't I use a range notation. I wanted to fire a different validation message for each min fail and max fail. I looked into this and had no look. I'd also have to pass in the range as a datetime and its static or something so I couldn't have done DateTime.Now.AddYears(-90) for instance.
My problems
I'm a noob at MVC, JQuery validation and the whole MVC architecture!
What I've come up with works. However, as you can see there is alot repeated code, I'd like to conform to DRY.
My first hack was to pass in the value that i'm checking against into the validation message. I got around this by doing...
[MaximumAgeCheck(90,"You have to be at most {0} to apply")]
and inside the validation attribute
private readonly int _min;
private readonly string _defaultErrorMessage = "";
public MinimumAgeCheck(int min, string defaultErrorMessage)
: base(defaultErrorMessage)
{
_min = min;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", _min.ToString());
}
and I used it for instance like so..
return new ValidationResult(_defaultErrorMessage);
I know this isn't the right way to do it, and wondered what is the best way to do this?
Second hack!
I'm passing in two validation parameters which I want to be able to access in the jQuery.validator.addMethod... method.
I tried to access these parameters by doing the following... params.[thevalueiadded], params[0]... etc, I even logged out params into console.log but it never showed me all the params, only the first value as a string!
My work around was to store the javascript variables at the top and load them from the adapters.add.
I'm probabily making little sense so here is the code, that works...I warn you, it is messy!
Model property and data annotation
[Required(ErrorMessage = "Date of birth required")]
[Display(Name = "Date of Birth")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
[DataType(DataType.DateTime, ErrorMessage = "Date of birth should be in dd/mm/yyyy format")]
[MinimumAgeCheck(18,"You have to be at least {0} to apply")]
[MaximumAgeCheck(90,"You have to be at most {0} to apply")]
public DateTime? DateOfBirth { get; set; }
Minimum Age Check and Maximum age check
validation attributes
public class MinimumAgeCheck : ValidationAttribute, IClientValidatable
{
private readonly int _min;
private readonly string _defaultErrorMessage = "";
public MinimumAgeCheck(int min, string defaultErrorMessage)
: base(defaultErrorMessage)
{
_min = min;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", _min.ToString());
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= _min && dtAge.Year <= 30))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, _min);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule mcvrTwo = new ModelClientValidationRule();
mcvrTwo.ValidationType = "checkminimumage";
mcvrTwo.ErrorMessage = _defaultErrorMessage;
mcvrTwo.ValidationParameters.Add("todaysdate", DateTime.Now.ToString("dd/MM/yyyy"));
mcvrTwo.ValidationParameters.Add("lowerage", _min.ToString());
return new List<ModelClientValidationRule> { mcvrTwo };
}
}
public class MaximumAgeCheck : ValidationAttribute, IClientValidatable
{
private readonly int Max;
private readonly string _defaultErrorMessage = "";
public MaximumAgeCheck(int max, string defaultErrorMessage)
: base(defaultErrorMessage)
{
Max = max;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", Max.ToString());
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= Max && dtAge.Year <= 30))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,Max);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule mcvrTwo = new ModelClientValidationRule();
mcvrTwo.ValidationType = "checkmaximumage";
mcvrTwo.ErrorMessage = _defaultErrorMessage;
mcvrTwo.ValidationParameters.Add("todaysdate", DateTime.Now.ToString("dd/MM/yyyy"));
mcvrTwo.ValidationParameters.Add("upperage", Max.ToString());
return new List<ModelClientValidationRule> { mcvrTwo };
}
}
The Jquery
(function ($) {
var mintodaysDateVal;
var maxtodaysDateVal;
var lowerageVal;
var upperageVal;
jQuery.validator.unobtrusive.adapters.add("checkminimumage", ['lowerage', 'todaysdate', 'upperage'], function (options) {
options.rules["checkminimumage"] = options.params;
mintodaysDateVal = options.params.todaysdate;
lowerageVal = options.params.lowerage;
options.messages["checkminimumage"] = options.message;
});
jQuery.validator.addMethod("checkminimumage", function (value, element, params) {
var currDate = mintodaysDateVal;
var sdoc = currDate.split('/');
var dobDate = value;
var sdob = dobDate.split('/');
//pass year,month,date in new Date object.
var vDOB = new Date(sdob[2], sdob[1] - 1, sdob[0]);
var vDOC = new Date(sdoc[2], sdoc[1] - 1, sdoc[0]);
//getAge user define function to calculate age.
var vYrs = getAge(vDOB, vDOC);
var result = false;
if (vYrs >= lowerageVal) { result = true; }
return result;
});
jQuery.validator.unobtrusive.adapters.add("checkmaximumage", ['lowerage', 'todaysdate', 'upperage'], function (options) {
options.rules["checkmaximumage"] = options.params;
maxtodaysDateVal = options.params.todaysdate;
upperageVal = options.params.upperage;
options.messages["checkmaximumage"] = options.message;
});
jQuery.validator.addMethod("checkmaximumage", function (value, element, params) {
var currDate = maxtodaysDateVal;
var sdoc = currDate.split('/');
var dobDate = value;
var sdob = dobDate.split('/');
var vDOB = new Date(sdob[2], sdob[1] - 1, sdob[0]);
var vDOC = new Date(sdoc[2], sdoc[1] - 1, sdoc[0]);
var vYrs = getAge(vDOB, vDOC);
var result = false;
if (vYrs <= upperageVal) { result = true; }
return result;
});
function getAge(oldDate, currDate) {
return currDate.getFullYear() - oldDate.getFullYear();
}
} (jQuery));
I hope this makes sense, I've read it over and its quite garbled... so i'll be happy to answer any comments.
Really useful code, but the ValidationResult IsValid method has some bugs. It doesn't handle future dates or blank dates. Plus it seems to have a hard coded limit to age 30 in - looks to be debug code? Anyway, I addressed those issues for my code and came up with the below:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return new ValidationResult(_defaultErrorMessage);
}
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
if (lTicks < 0)
{
return new ValidationResult(_defaultErrorMessage);
}
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= _min ))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
Take a look at the MVC Foolproof Validation library. You can find it in NuGet.
It has pretty much all the validation you need and is added via data annotations. It will intergrate nicely into the unobtrusive client side validation.
I had two fields some thing like phone number and mobile number. Some thing like..
[Required]
public string Phone { get; set; }
[Required]
public string Mobile{ get; set; }
But user can enter data in either one of it. One is mandatory. How to handle them i.e how to disable the required field validator for one field when user enter data in another field and viceversa. In which event i have to handle it in javascript and what are the scripts i need to add for this. Can anyone please help to find the solution...
One possibility is to write a custom validation attribute:
public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
"Unknown property {0}",
new[] { _otherProperty }
));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
{
if (value == null || value as string == string.Empty)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
FormatErrorMessage(validationContext.DisplayName),
new[] { _otherProperty }
));
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
rule.ValidationParameters.Add("other", _otherProperty);
yield return rule;
}
}
which you would apply to one of the properties of your view model:
public class MyViewModel
{
[RequiredIfOtherFieldIsNull("Mobile")]
public string Phone { get; set; }
public string Mobile { get; set; }
}
then you could have a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view in which you will register an adapter to wire the client side validation for this custom rule:
#model MyViewModel
<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">
jQuery.validator.unobtrusive.adapters.add(
'requiredif', ['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
}
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
}
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
options.rules['requiredif'] = element;
if (options.message) {
options.messages['requiredif'] = options.message;
}
}
);
jQuery.validator.addMethod('requiredif', function (value, element, params) {
var otherValue = $(params).val();
if (otherValue != null && otherValue != '') {
return true;
}
return value != null && value != '';
}, '');
</script>
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Phone)
#Html.EditorFor(x => x.Phone)
#Html.ValidationMessageFor(x => x.Phone)
</div>
<div>
#Html.LabelFor(x => x.Mobile)
#Html.EditorFor(x => x.Mobile)
#Html.ValidationMessageFor(x => x.Mobile)
</div>
<button type="submit">OK</button>
}
Pretty sick stuff for something so extremely easy as validation rule that we encounter in our everyday lives. I don't know what the designers of ASP.NET MVC have been thinking when they decided to pick a declarative approach for validation instead of imperative.
Anyway, that's why I use FluentValidation.NET instead of data annotations to perform validations on my models. Implementing such simple validation scenarios is implemented in a way that it should be - simple.
I know this question is not so hot, because it was asked relatively long time ago, nevertheless I'm going to share with a slightly different idea of solving such an issue. I decided to implement mechanism which provides conditional attributes to calculate validation results based on other properties values and relations between them, which are defined in logical expressions.
Your problem can be defined and automatically solved by the usage of following annotations:
[RequiredIf("Mobile == null",
ErrorMessage = "At least email or phone should be provided.")]
public string Phone{ get; set; }
[RequiredIf("Phone == null",
ErrorMessage = "At least email or phone should be provided.")]
public string Mobile { get; set; }
If you feel it would be useful for your purposes, more information about ExpressiveAnnotations library can be found here. Client side validation is also supported out of the box.
Since nobody else suggested it, I'm going to tell you a different way to do this that we use.
If you create a notmapped field of a custom data type (in my example, a pair of gps points), you can put the validator on that and you don't even need to use reflection to get all the values.
[NotMapped]
[DCGps]
public GPS EntryPoint
{
get
{
return new GPS(EntryPointLat, EntryPointLon);
}
}
and the class, a standard getter/setter
public class GPS
{
public decimal? lat { get; set; }
public decimal? lon { get; set; }
public GPS(decimal? lat, decimal? lon)
{
this.lat = lat;
this.lon = lon;
}
}
and now the validator:
public class DCGps : DCValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (!(value is GPS)) {
return new ValidationResult("DCGps: This annotation only works with fields with the data type GPS.");
}
//value stored in the field.
//these come through as zero or emptry string. Normalize to ""
string lonValue = ((GPS)value).lonstring == "0" ? "" : ((GPS)value).lonstring;
string latValue = ((GPS)value).latstring == "0" ? "" : ((GPS)value).latstring;
//place validation code here. You have access to both values.
//If you have a ton of values to validate, you can do them all at once this way.
}
}
I am writing a custom validation attribute
It does conditional validation between two fields
When I create my rule, one of the things that I could not solve is how to pass javascript code through ValidationParameters
Usually, I just do
ValidationParameters["Param1"] = "{ required :function(element) { return $("#age").val() < 13;) }"
However, the MicrosoftMvcJQueryValidation.js routines trnasforms this to
Param1 = "{ required :function(element) { return $("#age").val() < 13;) }"
I could use Param1.eval() in Javascript. This will evaluates and executes the code but I just want to evalute the code and execute it later
JSON parser does not parse string contening Javascript code
So I am asking here for any idea
Not sure how you would inject javascript as you describe, but you may want to consider using the custom validation pattern for ASP.NET MVC 2.
Important pieces are the ValidationAttribute, DataAnnotationsModelValidator, registering the validator in Application_Start with DataAnnotationsModelValidatorProvider.RegisterAdapter, and the client side Sys.Mvc.ValidatorRegistry.validators function collection to register your client side validation code.
Here's the example code from my post.
[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
public string Password { get; set; }
[StringLength(128, ErrorMessage = "Must be under 128 characters.")]
[MinStringLength(3, ErrorMessage = "Must be at least 3 characters.")]
public string PasswordAnswer { get; set; }
public class MinStringLengthAttribute : ValidationAttribute
{
public int MinLength { get; set; }
public MinStringLengthAttribute(int minLength)
{
MinLength = minLength;
}
public override bool IsValid(object value)
{
if (null == value) return true; //not a required validator
var len = value.ToString().Length;
if (len < MinLength)
return false;
else
return true;
}
}
public class MinStringLengthValidator : DataAnnotationsModelValidator<MinStringLengthAttribute>
{
int minLength;
string message;
public MinStringLengthValidator(ModelMetadata metadata, ControllerContext context, MinStringLengthAttribute attribute)
: base(metadata, context, attribute)
{
minLength = attribute.MinLength;
message = attribute.ErrorMessage;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ErrorMessage = message,
ValidationType = "minlen"
};
rule.ValidationParameters.Add("min", minLength);
return new[] { rule };
}
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MinStringLengthAttribute), typeof(MinStringLengthValidator));
}
Sys.Mvc.ValidatorRegistry.validators["minlen"] = function(rule) {
//initialization
var minLen = rule.ValidationParameters["min"];
//return validator function
return function(value, context) {
if (value.length < minLen)
return rule.ErrorMessage;
else
return true; /* success */
};
};