I am trying to implement custom attribute validation, similar to one demonstrated here in ScottGu's blog:
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
I have this custom validator attribute for Email:
public class EmailAttribute : RegularExpressionAttribute
{
public EmailAttribute() :
base("^[A-Za-z0-9](([_\\.\\-]?[a-zA-Z0-9]+)*)#([A-Za-z0-9]+)(([\\.\\-]?[a-zA-Z0-9]+)*)\\.([A-Za-z]{2,})$") { }
}
My class uses it like this:
[Required(ErrorMessage = ValidationCiM.MsgObaveznoPolje)]
[Email(ErrorMessage = ValidationCiM.MsgMailNeispravan)]
[StringLength(ValidationCiM.LenSrednjePolje, ErrorMessage = ValidationCiM.MsgSrednjePolje)]
public string Mail { get; set; }
and it all works well on server side, model is validated ok, and everything. But client side validation does not activate for this the second attribute, it works for Required, and it also works for StringLength but not for Email.
i have tried including both jquery and Microsoft ajax scripts, but there seems to be no difference.
In ScottGu's blog, he states that the custom validation if implemented like this should work without the need to add custom script.
Any ideas please?
Use IClientValidatable in ASP.NET MVC 3:
public class EmailAttribute : RegularExpressionAttribute, IClientValidatable
{
public EmailAttribute()
:base(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})$")
{
}
public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRegexRule(this.ErrorMessageString, base.Pattern);
return new[] { rule };
}
}
What you actually needed to do was this (on application start):
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAttribute), typeof(RegularExpressionAttributeAdapter));
It hooks up the client validation to your custom attribute.
Related
I am performing custom validation on a model property. The property is a proxy for other parts of the model and therefore requires no explicit user input. Server-side validation is working correctly but no client-side rules are generated.
I have been able to successfully generate the client rules but only when 'referencing the property in the view' using TextBoxFor, CheckBoxFor (or perhaps more appropriately HiddenFor) on the target property. However this feels like a hack, since the property doesn't even have a setter, so the value is guaranteed to be discarded.
Is there any way to force ASP.NET MVC to generate the client validation rules for a specific property without it being used in the view?
Example Code
public class Model {
public bool Option1 { get; set; }
public bool Option2 { get; set; }
public bool Option3 { get; set; }
[CustomValidator(ErrorMessage = "Validation Failed!")]
public bool AtLeastOneSelected => Option1 != false || Option2 != false || Option3 != false;
}
public class CustomValidator : ValidationAttribute, IClientValidatable {
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
// breakpoint below
return new List<ModelClientValidationRule>();
}
}
Example View
#Html.CheckBoxFor(m => m.Option1)
#Html.CheckBoxFor(m => m.Option2)
#Html.CheckBoxFor(m => m.Option3)
#Html.ValidationMessageFor(m => m.AtLeastOneSelected)
#*//Client rules will not be generated without this line*#
#*//#Html.TextBoxFor(m => m.AtLeastOneSelected)*#
Your sole problem here is that client-side validation doesn't work out of the box. This is because the validation client-side must be tied to a form field. There has to be something that triggers an valid/invalid determination, and without a form field you have nothing to do that with.
You can always write some custom client-side validation. Just check of your options and see if at least one is set, then you add/remove a message accordingly. You can check the jQuery Validation docs (what's behind client-side validation in MVC) for require_from_group. Seems like pretty much what you want in this particular example. You'll just have to add the rule manually.
$( "#myform" ).validate({
rules: {
Option1: {
require_from_group: [1, ".options"]
},
Option2: {
require_from_group: [1, ".options"]
},
Option3: {
require_from_group: [1, ".options"]
}
}
});
Then, you just need to add that class to each of your option fields.
I had a similar issue to yours in that I had to validate three inputs in addition to their own validation due to some silly UI requirements.
I did manage to make it work with all the built-in stuff and without any need for a hack.
I needed a ValidationAttribute
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;
namespace ValidationAttributes
{
public sealed class MyValidationRule : ValidationAttribute, IClientValidatable
{
public MyValidationRule()
: base("My validation rule's error message {0}")
{
// initialise vars here
}
public override bool IsValid(object value)
{
var valid = value == null;
// validate logic here
return valid;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationMyValidationRule(FormatErrorMessage("SomeValueFor0ParamAbove"), MinimumAgeInYears);
}
}
}
... and a ModelClientValidationRule
using System.Globalization;
using System.Web.Mvc;
namespace ModelClientValidationRules
{
public class ModelClientValidationMyValidationRule : ModelClientValidationRule
{
public ModelClientValidationMyValidationRule(string errorMessage)
{
ErrorMessage = errorMessage;
ValidationType = "myvalidatorrule";
// add any params needed on the client side from the server side by using the following
ValidationParameters.Add("param_name", "param_value");
}
}
}
... and I needed to add a prop in my Model
[MyValidationRule]
public whatever SomeProperty { get; set; }
... and I needed to add the client side validation in the view
#Html.ValidationMessageFor(x => x.SomeProperty, "Validation message")
... and finally I needed to include some js in my scripts
jQuery.validator.addMethod("myvalidationrule", function (value, element, params) {
var valid = false;
// your validation logic goes here
// NB element will be undefined as the validator is not driven by an element
return valid;
});
jQuery.validator.unobtrusive.adapters.add("myvalidationrule", function (options) {
options.rules["myvalidationrule"] = options.params;
options.message["myvalidationrule"] = options.message;
});
I really hope this makes sense... :S
How can I create a custom validation attribute with client side validation without implementing IClientValidatable?
How does System.ComponentModel.DataAnnotations.RequiredAttribute client side validate?
The reason to do this is because I'm using objects from classes in another project as models in my views and I don't want to add the System.Web.MVC reference to that project.
EDIT to add more information:
I know that IClientValidatable is used to add custom attributes to
the HTML to be used later by the unobtrusive validation.
I know I'll need to add the javascript code to made the validation in
the client.
What I don't know is how to use the information from the custom validation attribute to add the necessary attributes to the HTML for unobtrusive validation to work.
This is my custom validation attribute:
public class RequiredGuidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
Guid? guidValue = value as Guid?;
if (guidValue == null)
return false;
return guidValue != Guid.Empty;
}
}
This is my property with the attribute applied:
[RequiredGuid(ErrorMessageResourceType = typeof(ClientOrderResources), ErrorMessageResourceName = "RequiredShippingMethod")]
public Guid ShippingMethodId
{
get { return GetProperty(ShippingMethodIdProperty); }
set { SetProperty(ShippingMethodIdProperty, value); }
}
And finally I'm rendering a hidden input for that property in the view using Html.HiddenFor.
Now, how can I get the error message from the attribute to apply it to the HTML? Should I do it my self using Reflection or there is a better way?
And then how can I tell Html.HiddenFor to use that information to add the necessary attributes to the HTML?
We had a similar problem. We have a model we use for our account creation that uses IClientValidatable on its custom attributes. However, we created a batch account creation process that sits outside of the website that we weren't able to reference System.Web.Mvc in. Because of this, when we called Validator.TryValidateObject, any custom validator that inherited from IClientValidatable was simply skipped. Here's what we were working with that was failing to validate outside of our website:
public class AgeValidatorAttribute : ValidationAttribute, IClientValidatable
{
public int AgeMin { get; set; }
public int AgeMax { get; set; }
public override bool IsValid(object value)
{
//run validation
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "agevalidator"
};
rule.ValidationParameters["agemin"] = AgeMin;
rule.ValidationParameters["agemax"] = AgeMax;
yield return rule;
}
Removing System.Web.Mvc required us to also remove GetClientValidationRules and the IClientValidatable reference. In order to do this and still have client side validation, we had to create a new class:
public class AgeValidatorClientValidator : DataAnnotationsModelValidator<AgeValidatorAttribute>
{
private readonly string _errorMessage;
private readonly string _validationType;
public AgeValidatorClientValidator(ModelMetadata metadata, ControllerContext context, AgeValidatorAttribute attribute)
: base(metadata, context, attribute)
{
this._errorMessage = attribute.FormatErrorMessage(metadata.DisplayName);
this._validationType = "agevalidator";
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this._errorMessage,
ValidationType = this._validationType
};
rule.ValidationParameters["agemin"] = base.Attribute.AgeMin;
rule.ValidationParameters["agemax"] = base.Attribute.AgeMax;
yield return rule;
}
}
As you can see, it does essentially the same thing as it used to, it's just done using the DataAnnatotationsModelValidator rather than IClientValidatable. There's one more step we need to do to actually attach the DataAnnotationsModelValidator to the atttribute, and that's done in the Global.asax.cs Application_Start method
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof(AgeValidatorAttribute), typeof(AgeValidatorClientValidator));
Now you can use this just as you would use a normal attribute:
[AgeValidator(AgeMax = 110, AgeMin = 18, ErrorMessage = "The member must be between 18 and 110 years old")]
public string DateOfBirth { get; set; }
I know this question is a year old, but I spent all day yesterday and half of today trying to figure this issue out. So I hope this helps somebody who runs into the same problem if OP hasn't figured the answer out yet.
Please note, I did not include any javascript in this writeup as it required no changes from the standard implementation of custom validation rules using jQuery.validate.
You can't have custom validation on the client unless you implement IClientValidatable. And for that you also need to add client script as well.
http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvccustomvalidation_topic3.aspx
It is possible, i found this article on how to do it:
http://xhalent.wordpress.com/2011/05/12/custom-unobstrusive-jquery-validation-in-asp-net-mvc-3-using-dataannotationsmodelvalidatorprovider/
basically you have to create a DataAnnotationsModelValidator on your client an register it in Application_Start().
And don't forget that you still have to write the Javascript for client side validation.
Scenario :-
I am developing MVC 4 application, the website will run in several languages and will be hosted on Azure.
For localizing we are relying on the database rather than resource bundle approach.
Problem :-
I want to customize error messages at runtime, I want to localize the messages through the database.
I have tried to change attribute values through reflection but it had not worked.
Code :-
//Model
public class Home
{
[Required(ErrorMessage = "Hard coded error msg")]
public string LogoutLabel { get; set; }
}
//On controller
public ActionResult Index()
{
Home homeData = new Home();
foreach (PropertyInfo prop in homeData.GetType().GetProperties())
{
foreach (Attribute attribute in prop.GetCustomAttributes(false))
{
RequiredAttribute rerd = attribute as RequiredAttribute;
if (rerd != null)
{
rerd.ErrorMessage = "dynamic message";
}
}
}
return View(homeData);
}
On client side when validation takes place it shows me old message "Hard Coded error msg".
Please suggest how this can be customised if we donot want to use Resource bundle approach
You would better create and register your own DataAnnotationsModelMetadataProvider where you can just override the error messages. For more detail please see the answers to the similar question here MVC Validation Error Messages not hardcoded in Attributes
are you intended to implement this nested loop to localize validation message of all you'r entities ? i think no.a better solution is using Validator attribute.
for you'r class :
[Validator(typeof(HomeValidator))]
public class Home
{
public string LogoutLabel { get; set; }
}
now lets implement HomeValidator :
public class HomeValidator : AbstractValidator<Home>
{
public HomeValidator()
{
RuleFor(x => x.LogoutLabel ).NotEmpty().WithMessage("your localized message");
}
}
I've read Phil Haack's post on custom client-side validation in ASP.NET MVC 2. I want to do the same thing but with the jQuery adapter and using ASP.NET MVC 2 RC (as opposed to MVC 2 Beta that the post uses). Has anyone been able to figure how to do this?
I specially want to implement the password matching validation (i.e. password & confirm password must match). The ASP.NET MVC 2 RC VS.NET project template does show how to implement that on the server-side (using the PropertiesMustMatchAttribute) but not on the client-side.
I assume you already followed Phil Haack's instructions here http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx) on how to get custom validation working with MS AJAX client validation. To get it to work with jQuery, you'll need to modify the MicrosoftMvcJQueryValidation.js file:
In the __MVC_CreateRulesForField(validationField) function, you'll need to add a case statement. Continuing Phil's example, you'll need to add:
case "price":
__MVC_ApplyValidator_Price(rulesObj, thisRule.ValidationParameters["min"]);
break;
You'll then need to create the __MVC_ApplyValidator_Price function:
function __MVC_ApplyValidator_Price(object, value) {
// min is what jQuery Validate uses to validate for minimum values
object["min"] = value;
}
That should be enough to get Phil's example working.
Now, regarding your PropertiesMustMatchAttribute validation, it doesn't look like MVC generates the client-side json validation definition for attributes that decorate classes. Since PropertiesMustMatchAttribute must be used on the model (and not the property), I can't figure out how to make it trigger client-side validation. Instead, I took a different approach. I created a dummy validation attribute who's IsValid() overload always returns true, and used this attribute on a property. This is just a dummy attribute that will delegate the validation logic to jQuery validator's equalTo function. Here's the dummy attribute:
public class PropertiesMustMatchClientTriggerAttribute : ValidationAttribute
{
public string MatchProperty { get; set; }
public PropertiesMustMatchClientTriggerAttribute(string matchProperty)
{
MatchProperty = matchProperty;
ErrorMessage = "{0} doesn't match {1}.";
}
public override bool IsValid(object value)
{
return true;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, MatchProperty);
}
}
Here is the custom validator:
public class PropertiesMustMatchClientTriggerValidator : DataAnnotationsModelValidator<PropertiesMustMatchClientTriggerAttribute>
{
private string _message;
private string _matchProperty;
public PropertiesMustMatchClientTriggerValidator(ModelMetadata metaData, ControllerContext context, PropertiesMustMatchClientTriggerAttribute attribute)
: base(metaData, context, attribute)
{
_message = attribute.FormatErrorMessage(metaData.DisplayName);
_matchProperty = attribute.MatchProperty;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ErrorMessage = _message,
ValidationType = "equalTo"
};
rule.ValidationParameters.Add("matchField", _matchProperty);
return new[] { rule };
}
}
the above custom validator needs to be registered in Application_Start() per Phil's blog:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PropertiesMustMatchClientTriggerAttribute), typeof(PropertiesMustMatchClientTriggerValidator));
Finally, you need to modify the MicrosoftMvcJQueryValidation.js file:
Add the following case statement to __MVC_CreateRulesForField:
case "equalTo":
__MVC_ApplyValidator_EqualTo(rulesObj, thisRule.ValidationParameters["matchField"]);
break;
add this function:
function __MVC_ApplyValidator_EqualTo(object, elemId) {
object["equalTo"] = document.getElementById(elemId);
}
Now you need to attach the dummy validation attribute to a property:
[PropertiesMustMatchClientTrigger("Password")]
public string ConfirmPassword { get; set; }
That should do it.
Creating this dummy attribute is a bit ugly, so I hope someone can come up with a more elegant solution.
Here's how to add a custom jQuery validation:
$.validator.addMethod("noSpaces", function(value, element) {
if ($(element).val().indexOf(" ") >= 0) {
return false;
} else {
return true;
}
}, "Value must not contain spaces");
I know this is an old post, and you are/were using MVC2, but MVC3 now comes with the CompareAttribute, which can be used for your use case of password confirmation matching.
Source: http://www.nickriggs.com/posts/asp-net-mvc-3-data-annotations-provide-property-level-contingent-validation/
I'd like to create a custom validation attribute for MVC2 for an email address that doesn't inherit from RegularExpressionAttribute but that can be used in client validation. Can anyone point me in the right direction?
I tried something as simple as this:
[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )]
public class EmailAddressAttribute : RegularExpressionAttribute
{
public EmailAddressAttribute( )
: base( Validation.EmailAddressRegex ) { }
}
but it doesn't seem to work for the client. However, if I use RegularExpression(Validation.EmailAddressRegex)] it seems to work fine.
You need to register an adapter for the new attribute in order to enable client side validation.
Since the RegularExpressionAttribute already has an adapter, which is RegularExpressionAttributeAdapter, all you have to do is reuse it.
Use a static constructor to keep all the necessary code within the same class.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class EmailAddressAttribute : RegularExpressionAttribute
{
private const string pattern = #"^\w+([-+.]*[\w-]+)*#(\w+([-.]?\w+)){1,}\.\w{2,4}$";
static EmailAddressAttribute()
{
// necessary to enable client side validation
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAddressAttribute), typeof(RegularExpressionAttributeAdapter));
}
public EmailAddressAttribute() : base(pattern)
{
}
}
For more information checkout this post explaining the complete process.
http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx
The CustomValidationAttribute Class MSDN page has a few examples on it now. The Phil Haacked post is out of date.
Look at the universal Dependent Property Validator in this article
Have you tried using Data Annotations?
This is my Annotations project
using System.ComponentModel.DataAnnotations;
public class IsEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
//do some checking on 'value' here
return true;
}
}
This is in my Models project
namespace Models
{
public class ContactFormViewModel : ValidationAttributes
{
[Required(ErrorMessage = "Please provide a short message")]
public string Message { get; set; }
}
}
This is my controller
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ContactUs(ContactFormViewModel formViewModel)
{
if (ModelState.IsValid)
{
RedirectToAction("ContactSuccess");
}
return View(formViewModel);
}
You'll need to google DataAnnotations as you need to grab the project and compile it. I'd do it but I need to get outta here for a long w/end.
Hope this helps.
EDIT
Found this as a quick google.