MVC disable unobtrusive validation for specific validator - asp.net-mvc

I'm currently building a MVC4 project which uses unobtrusive validation through data annotations.
I have a specific field, "PostalCode", which is both [Required] and has a [RegularExpression] validator attached to it.
The thing is, this regular expression should only be verified if a specific country is selected. (This country will be the default value and we can assume in nearly all cases this will be used)
Now I need some way to disable this regex validation when a different country is selected, while keeping the required validator active.
Nearly all sollutions I've found are using a jQuery.validator.defaults.ignore filter, but this would disable both validators on that item.
Any thoughts on how to best tackle this problem?
Edit: Small code snippet to show how this is working.
[Required]
[RegularExpression("^[1-9]\\d{3} ?[a-zA-Z]{2}$"] //Should only be verified if Country == "The Netherlands"
string PostalCode{get;set;}
[Required]
string Country {get;set;}

In the end I made up my own ValidationAttribute based on this blog post: http://thewayofcode.wordpress.com/2012/01/18/custom-unobtrusive-jquery-validation-with-data-annotations-in-mvc-3/
It's an elegant sollution and required way less work than I anticipated.
Edit: As per request, I provide the sollution created by myself:
// DependentRegularExpressionAttribute.cs
/// <summary>
/// Only performs a regular expression validation if a specified other property meets a validation requirement
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DependentRegularExpressionAttribute : ValidationAttribute, IClientValidatable
{
private readonly Regex _regex;
private readonly string _otherPropertyName;
private readonly Regex _otherPropertyRegex;
public DependentRegularExpressionAttribute(string regex, string otherPropertyName, string otherPropertyRegex)
{
_regex = new Regex(regex);
_otherPropertyName = otherPropertyName;
_otherPropertyRegex = new Regex(otherPropertyRegex);
}
/// <summary>
/// Format the error message filling in the name of the property to validate and the reference one.
/// </summary>
/// <param name="name">The name of the property to validate</param>
/// <returns>The formatted error message</returns>
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, _regex, _otherPropertyName, _otherPropertyRegex);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var validationResult = ValidationResult.Success;
if (value == null || String.IsNullOrEmpty(value as string))
return validationResult;
// Using reflection we can get a reference to the other property
var otherPropertyInfo = validationContext.ObjectType.GetProperty(_otherPropertyName);
var otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || String.IsNullOrEmpty(otherPropertyValue as string))
return validationResult;
if (_otherPropertyRegex.IsMatch(otherPropertyValue.ToString()))
{
if (!_regex.IsMatch(value.ToString()))
validationResult = new ValidationResult(ErrorMessage);
}
return validationResult;
}
#region IClientValidatable Members
/// <summary>
///
/// </summary>
/// <param name="metadata"></param>
/// <param name="context"></param>
/// <returns></returns>
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = FormatErrorMessage(metadata.DisplayName ?? metadata.PropertyName);
// The value we set here are needed by the jQuery adapter
var dependentRegexRule = new ModelClientValidationRule
{
ErrorMessage = errorMessage,
ValidationType = "dependentregex"
};
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dependentRegexRule.ValidationParameters.Add("otherpropertyname", _otherPropertyName);
dependentRegexRule.ValidationParameters.Add("regex", _regex);
dependentRegexRule.ValidationParameters.Add("otherpropertyregex", _otherPropertyRegex);
yield return dependentRegexRule;
}
#endregion
}
// customvalidation.js
$.validator.addMethod("dependentregex", function (value, element, params) {
var regex = new RegExp(params[0]);
var otherElement = document.getElementById(params[1]);
var otherElementRegex = new RegExp(params[2]);
if (!value || !otherElement.value)
return true;
if (otherElementRegex.test(otherElement.value)) {
if (!regex.test(element.value))
return false;
}
return true;
});
$.validator.unobtrusive.adapters.add("dependentregex", ["regex", "otherpropertyname", "otherpropertyregex"], function(options) {
options.rules["dependentregex"] = [options.params.regex,
options.params.otherpropertyname,
options.params.otherpropertyregex];
options.messages["dependentregex"] = options.message;
});
Inside my viewmodel I do the following:
[Display(Name = "Customer_PostalCode", ResourceType = typeof(Resources.DisplayNames))]
[DependentRegularExpression("^[1-9]\\d{3} ?[a-zA-Z]{2}$", "CorrespondenceCountry", "Nederland", ErrorMessageResourceType = typeof(Resources.Validation), ErrorMessageResourceName = "Customer_PostalCode")] //"Nederland" is a regular expression in this case
[Required(ErrorMessageResourceType = typeof(Resources.Validation), ErrorMessageResourceName = "Shared_RequiredField")]
public string CorrespondenceZipCode { get; set; }
In the end, the customvalidation.js method basically does the exact same thing as the C# code. A detailed explanation of what everything does can be found in the blogpost I referenced

It seems like you want a "required if" validation attribute. I would check out http://foolproof.codeplex.com/ - it has an implementation that you can use like so (lifted directly from the project's site):
private class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public bool Married { get; set; }
[RequiredIfTrue("Married")]
public string MaidenName { get; set; }
}

Take a look at this, I haven't used it myself, but it seems to suit your needs http://foolproof.codeplex.com/workitem/18974
They have an example that looks like this:
[RequiredIf("Country", Operator.RegExMatch, "(1033|4105)", ErrorMessage = "{0} is required")]
public string State { get; set; }
[Required(ErrorMessage = "{0} is required")]
public int Country { get; set; }

Related

Model a link in umbraco using Glass Mapper

I am using Glass Mapper for Umbraco. While trying to model something I have a class like:
[UmbracoType(AutoMap = true)]
public class ImageWithLink : BaseUmbracoItem
{
public virtual Image Image { get; set; }
public virtual ?? Link { get; set; }
public virtual string Copy { get; set; }
}
There does not seem to be a 'Link' data type like there is in the Sitecore implementation. I saw This post ( http://bluetubeinc.com/blog/2014/6/glass-mapper-and-umbraco-7) and they use the 'RelatedLink' data type, but that does not exsist ( i checked in the glass repository).
Do I have to model it my self?
Edit: This is the Related Links property type.
Assuming that Umbraco hasn't changed in a while and that you mean a link as in a html anchor...
I may be wrong on this one, but from memory, Umbraco has / had no concept of a link when it comes to the return from the api. It is returned as a node id (or list of) and you must use an extension method to return the url from.
Like you, as far as I can see in the Glass code base, it doesn't have an explicit implementation for a 'link' type like we have in Sitecore.
My guess is that you would have to either roll your own using a custom class and the Delegate feature OR use an integer mapping and call the umbraco api method.
I would also guess is that RelatedLink in that example is mapping to another class which would use the UmbracoPropertyTypeMapper in a similar way to what we do in Sitecore between types, not a 'Link' as in anchor.
We are due to look at umbraco again during the V4 process I believe so talk to Mike about adding it as a feature.
I Found a (rather horrible) solution. Umbraco returns Json, so i had to de-serialize it. You could turn this into a mapper.
[UmbracoType(AutoMap = true)]
public class BaseUmbracoItem : IUmbracoItem
{
public virtual string Links { get; set; }
public List<UmbracoLink> TypedLink
{
get
{
return JsonConvert.DeserializeObject<List<UmbracoLink>>(Links);
}
}
}
public class UmbracoLink
{
public string link { get; set; }
public string type { get; set; }
public string title { get; set; }
public bool newWindow { get; set; }
public bool isInternal { get; set; }
}
Here is a mapper version:
public class UmbracoLinkMapper : AbstractDataMapper
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoLinkMapper"/> class.
/// </summary>
public UmbracoLinkMapper()
{
ReadOnly = true;
}
/// <summary>
/// Maps data from the .Net property value to the CMS value
/// </summary>
/// <param name="mappingContext">The mapping context.</param>
/// <returns>The value to write</returns>
/// <exception cref="System.NotSupportedException"></exception>
public override void MapToCms(AbstractDataMappingContext mappingContext)
{
throw new NotSupportedException();
}
/// <summary>
/// Maps data from the CMS value to the .Net property value
/// </summary>
/// <param name="mappingContext">The mapping context.</param>
/// <returns>System.Object.</returns>
public override object MapToProperty(AbstractDataMappingContext mappingContext)
{
var scContext = mappingContext as UmbracoDataMappingContext;
var scConfig = Configuration as UmbracoLinkConfiguration;
var properties = scContext.Content.Properties.Where(x => x.Alias == Configuration.PropertyInfo.Name.ToLower()).ToList();
if (properties.Any())
{
var property = properties.First().Value as string;
return JsonConvert.DeserializeObject<List<UmbracoLink>>(property).First();
}
return null;
}
/// <summary>
/// Indicates that the data mapper will mapper to and from the property
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="context">The context.</param>
/// <returns><c>true</c> if this instance can handle the specified configuration; otherwise, <c>false</c>.</returns>
public override bool CanHandle(AbstractPropertyConfiguration configuration, Context context)
{
return configuration is UmbracoLinkConfiguration;
}
}
public class UmbracoLinkConfiguration : AbstractPropertyConfiguration
{
public bool IsLazy { get; set; }
public bool InferType { get; set; }
}
public class UmbracoLinkAttribute : AbstractPropertyAttribute
{
public bool IsLazy { get; set; }
public bool InferType { get; set; }
public override AbstractPropertyConfiguration Configure(PropertyInfo propertyInfo)
{
var config = new UmbracoLinkConfiguration { IsLazy = IsLazy, InferType = InferType };
Configure(propertyInfo, config);
return config;
}
}

Customizing DisplayFormat beyond DataFormatString

I have a MVC 4 project where I would like to use functionality similar to DisplayFromat, but setting a DataFormatString is not enough. I would like a function to be called to format the string. Is that possible?
I have tested inheriting DisplayFormat but that just lets me set the DataFormatString.
I have looked at customizing DataAnnotationsModelMetadataProvider, but I don't see how I would make it call a custom function for formatting.
My particular case is that I need to format the integer 201351 as "w51 2013". I couldn't come up with a format string that does that.
The easiest way is to expose a read-only property on your Model:
public class Model{
public int mydata{get; set;}
public string formattedDate{
get{
string formattedval;
// format here
return formattedval;
};
}
}
You can create a custom ValidationAttribute. Here is some code I use to validation someone has selected a drop down value.
using System.ComponentModel.DataAnnotations;
public sealed class PleaseSelectAttribute : ValidationAttribute
{
private readonly string _placeholderValue;
public override bool IsValid(object value)
{
var stringValue = value.ToString();
if (stringValue == _placeholderValue || stringValue == "-1")
{
ErrorMessage = string.Format("The {0} field is required.", _placeholderValue);
return false;
}
return true;
}
public PleaseSelectAttribute(string placeholderValue)
{
_placeholderValue = placeholderValue;
}
}
Then Use it:
[Required]
[Display(Name = "Customer")]
[PleaseSelect("Customer")]
public int CustomerId { get; set; }

MVC .NET [Required] DataAnnotations on a parent model causing invalid ModelState in a child model

I would like to "turn off" the Required field validation on a certain property of the parent model (VehicleManufacturer), when saving a child model (VehicleModel), i.e.:
public class VehicleManufacturer
{
public virtual Guid Id { get; set; }
[Required]
[StringLength(50, MinimumLength = 1)]
public virtual string Name { get; set; }
}
public class VehicleModel
{
public virtual Guid Id { get; set; }
[Required]
[StringLength(50, MinimumLength = 1)]
public virtual string Name { get; set; }
public virtual VehicleManufacturer Manufacturer { get; set; }
}
So, when I'm saving a new model, all I care about is it's Name and ManufacturerID, which would be selected from a drop-down list, however, because the ManufacturerName is marked [Required] in its entity, it invalidates my ModelState when saving a new VehicleModel, because ManufacturerName is null :(
I would like to know what is the best approach to this and how to do it.
I can think of a few solutions but none seem to be the right way:
Set a "default" ManufacturerName value before ModelState check, i.e.
"-" just to satisfy the DataAnnotation
Populate both ManufacturerName
and ManufacturerId in my VehicleModelView - not good if your parent
model has a bunch of required field you don't really need to use in a
child model
Turn off [Required] validation for child model (not sure
how?)
what do you think?
The simplest way is to have hidden fields for the required properties you don't want to show.
A possible solution is to add the foreign key column to the VehicleManufacturer (VehicleManufacturerId) to the VehicleModel and use that column in your view.
The IValidatableObject interface is for custom model validation.
For example:
public class VehicleModel : IValidatableObject
{
public virtual Guid Id { get; set; }
[StringLength(50, MinimumLength = 1)]
public virtual string Name { get; set; }
public virtual VehicleManufacturer Manufacturer { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(string.IsNullOrWhiteSpace(Name))
{
yield return new ValidationResult("Name is required")
}
}
}
Then in your controller call ModelState.IsValid
What I ended up doing is subclassing the parent model with something like ParentWithExtendedValidation : Parent
In ParentWithExtendedValidation all of the various [Required] Fields were tagged.
When I wanted to specifically edit the parent, I used type ParentWithExtendedValidation
-- since it is a subclass of Parent, once it passes model validation, you can cast it back to parent and pass it to your DAL with no issues.
When you are using it in a relationship e.g editing a child that references this, you can use the regular Parent class.
Hope this helps
EG I have a class Person -- with all of my normal virtual properties, and only the ID is required (to make validation that selects on person->ID still work right)
And a Class PersonEV
public class PersonWithExtendedValidation : Person
{
[Required]
public override string FullName { get; set; }
[Required]
public override string FirstName { get; set; }
[Required]
public override string LastName { get; set; }
[Required]
public override string DomainName { get; set; }
[Required]
public override string WorkPhone { get; set; }
[Required]
public override string CellPhone { get; set; }
[Required]
public override string Email { get; set; }
}
Then in your View/Controller work with e.g PersonEV for the model and argument.
Once you check for ModelState.IsValid , cast back to (Person) and pass to whatever repository or etc as normal
Ok. I looked at this and realized my method was wasteful.
How does this look?
I created an Annotation called ExtendedValidationRequired.
It has a static value that turns on conditional checking for extended values or not.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class ExtendedValidationRequiredAttribute : RequiredAttribute
{
// Summary:
// Initializes a new instance of the System.ComponentModel.DataAnnotations.RequiredAttribute
// class.
public ExtendedValidationRequiredAttribute()
{
}
// Summary:
// Checks that the value of the required data field is not empty.
//
// Parameters:
// value:
// The data field value to validate.
//
// Returns:
// true if validation is successful; otherwise, false.
//
// Exceptions:
// System.ComponentModel.DataAnnotations.ValidationException:
// The data field value was null.
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (ExtendedValidation.IsExtendedValidationEnabled(validationContext.ObjectType))
{
return base.IsValid(value,validationContext);
}
else
{
return ValidationResult.Success;
}
}
}
I then mark up my Sometimes required (eg when im directly editing that class) fields with [ExtendedValidationRequired(typeof(MyClassName))]
this works for other types of validators as well.
I actually went ahead and created a set of 'Sometimes On' validators and moved my setting to a separate class:
public static class ExtendedValidation
{
private static Dictionary extendedValidationExemptions = new Dictionary();
/// <summary>
/// Disable extended validation for a specific type
/// </summary>
/// <param name="type"></param>
public static void DisableExtendedValidation(Type type)
{
extendedValidationExemptions[type] = true;
}
/// <summary>
/// Clear any EV exemptions
/// </summary>
public static void Reset()
{
extendedValidationExemptions.Clear();
}
/// <summary>
/// Check if a class should perform extended validation
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsExtendedValidationEnabled(Type type)
{
if (extendedValidationExemptions.ContainsKey(type))
{
return false;
}
else
{
return true;
}
}
}
}
now i just turn ExtendedValidation off when editing children.
E.G: When editing a Child, Can DisableExtendedValidation(typeof(Parent)) and not get in the way.
Edit: Hrm, I realize this doesnt quite work. -- Can I determine what class I am looking at inside of a validationProperty? I guess I could always pass the parent property in but that is a PITA

Can I dynamically turn off validation in MVC?

I have a search form where I can search on one of two fields. Only one of the two is required. Is there a way to cleanly do this in the MVC provided validation?
If this is server validation, you could create a Custom Validation Attribute for this:
[ConditionalRequired("Field1","Field2")]
public class MyModel()
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Then you can create a custom validation attribute.. something like the following:
[AttributeUsage( AttributeTargets.Class, AllowMultiple = true, Inherited = true )]
public sealed class ConditionalRequiredAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "Either '{0}' or '{1}' must be provided.";
public string FirstProperty { get; private set; }
public string SecondProperty { get; private set; }
public ConditionalRequiredAttribute( string first, string second )
: base( _defaultErrorMessage )
{
FirstProperty = first;
SecondProperty = second;
}
public override string FormatErrorMessage( string name )
{
return String.Format( CultureInfo.CurrentUICulture, ErrorMessageString,
FirstProperty, SecondProperty );
}
public override bool IsValid( object value )
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( value );
string originalValue = properties.Find( FirstProperty, true ).GetValue( value ).ToString(); // ToString() MAY through a null reference exception.. i haven't tested it at all
string confirmValue = properties.Find( SecondProperty, true ).GetValue( value ).ToString( );
return !String.IsNullOrWhiteSpace( originalValue ) || !String.IsNullOrWhiteSpace( confirmValue ); // ensure at least one of these values isn't null or isn't whitespace
}
}
Its a little verbose, but it gives you the flexibility to able this attribute directly to your model as opposed to applying it to each individual field

How to validate two properties with ASP.NET MVC 2

I'm just getting started with ASP.NET MVC 2, and playing around with Validation.
Let's say I have 2 properties:
Password1
Password2
And I want to require that they are both filled in, and require that both are the same before the model is valid.
I have a simple class called "NewUser".
How would I implement that? I've read about ValidationAttribute, and understand that. But I don't see how I would use that to implement a validation that compares two or more properties against eathother.
Thanks in advance!
Problem with below solution:
When this is applied to an application, and the ModelBinder runs the validation of the Model, then there is a problem:
If a Property-level ValidationAttribute contains an error, then the Class-level ValidationAttribute's are NOT validated. I have not found a solution to this problem as of yet.
If you have a solution to this problem please share your experience. Thanks alot!
The default ASP.NET MVC 2 template of Visual Studio includes the exact validation attribute you need. Pasted from AccountModels.cs file :
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute {
private const string _defaultErrorMessage =
"'{0}' and '{1}' do not match.";
private readonly object _typeId = new object();
public PropertiesMustMatchAttribute(string originalProperty,
string confirmProperty)
: base(_defaultErrorMessage) {
OriginalProperty = originalProperty;
ConfirmProperty = confirmProperty;
}
public string ConfirmProperty { get; private set; }
public string OriginalProperty { get; private set; }
public override object TypeId {
get {
return _typeId;
}
}
public override string FormatErrorMessage(string name) {
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
OriginalProperty, ConfirmProperty);
}
public override bool IsValid(object value) {
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(value);
object originalValue = properties.Find(OriginalProperty,
true /* ignoreCase */).GetValue(value);
object confirmValue = properties.Find(ConfirmProperty,
true /* ignoreCase */).GetValue(value);
return Object.Equals(originalValue, confirmValue);
}
}
How to use :
[PropertiesMustMatch("Password", "ConfirmPassword",
ErrorMessage = "The password and confirmation password do not match.")]
class NewUser {
[Required]
[DataType(DataType.Password)]
[DisplayName("Password")]
public string Password { get; set; }
[Required]
[DataType(DataType.Password)]
[DisplayName("Confirm password")]
public string ConfirmPassword { get; set; }
}

Resources