I have a scenario which is actually answered here
That sample works perfect. I tried having it in my project where I added the exact server-side code for RequiredIf attribute (copy and paste). I also added the jquery.validate.js, jquery.validate.unobtrusive.js and added a script that adds the 'requiredif' method [$.validator.addMethod('requiredif',.......] and [$.validator.unobtrusive.adapters.add(........]
All copy and paste from the link above. Via firebug, I see all scripts loaded in my solution. The input fields all correctly identify the requiredif unobtrusive attribute via data-val-requiredif.
After hours of debugging, I noticed the only difference between the working sample and my sample is that in the working sample, when viewing via firebug, the rules collection does contain the requiredif rules and mine contains all (stringlength, etc) except for requiredif and explains why they don't fire.
The attribute RequiredIf has been added to the viewmodel and the code of the attribute is a straight copy from the link above. GetClientValidationRules method of the attribute is being called but the rules do not show up on the client-side.
Very bazaar. Any help in debugging is appreciated.
UPDATE
Here is the RequiredIf attribute:
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute _innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the property this validation depends upon
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(this.DependentProperty);
if (field != null)
{
// get the value of the dependent property
var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
// compare the value against the target value
if ((dependentvalue == null && this.TargetValue == null) ||
(dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
{
// match => means we should try validating this field
if (!_innerAttribute.IsValid(value))
// validation failed - return an error
return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
// find the value on the control we depend on;
// if it's a bool, format it javascript style
// (the default is True or False!)
string targetValue = (this.TargetValue ?? "").ToString();
if (this.TargetValue.GetType() == typeof(bool))
targetValue = targetValue.ToLower();
rule.ValidationParameters.Add("dependentproperty", depProp);
rule.ValidationParameters.Add("targetvalue", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
// build the ID of the property
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
// unfortunately this will have the name of the current field appended to the beginning,
// because the TemplateInfo's context has had this fieldname appended to it. Instead, we
// want to get the context as though it was one level higher (i.e. outside the current property开发者_开发知识库,
// which is the containing object (our Person), and hence the same level as the dependent property.
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
// strip it off again
depProp = depProp.Substring(thisField.Length);
return depProp;
}
}
GetClientValidationRules does get hit. A sample of the attribute added to the view model is:
[RequiredIf("Openid_Identifier", "", ErrorMessage = "Please choose a proper login.")]
[StringLength(50, ErrorMessage = "Email address is too long.")]
[RegularExpression(@"^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$", ErrorMessage = "Incorrect email format.")]
public string Email { get; set; }
StringLength and RegEx both work and I see the validation error. RequiredIf obviously does not. In the sample that does work from the original link, if I put the breakpoint inside
$.validator.addMethod('requiredif',
function (value, element, parameters) {
debugger;
var id = '#' + parameters['dependentproperty'];
It does get hit. In my solution, it never gets hit because of what I mentioned above (requiredif did not get added to the rules collection).
UPDATE 2
I'm using a parallel javascript loader (headjs) to load the javascript files in parallel. And if I have:
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/headjs/0.96/head.min.js"></script>
<script type="text/javascript">
head.js("http://ajax.aspnetcdn.com/ajax/jquery.validate/1.8.1/jquery.validate.min.js")
.js("http://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.validate.unobtrusive.min.js")
.js("http://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.unobtrusive-ajax.min.js")
.js("@Url.ScriptFromLocal("requiredif.js")");
</script>
Then it doesn't work. If I have regular script tags then all is good. I use the parallel loader everywhere and it works fine including out-of-the-box attributes that do work. Custom ones is a no go if I parallel load the js files.
Anything dynamic, whether it's dynamic html or dynamic javascript files, one needs to reparse the data with $.validator.unobtrusive.parse('#some_form_container') and all is well.
Thank you to the ones who responded.
精彩评论