Search code examples
asp.net-mvc-3data-annotationscustomvalidator

Asp.net MVC3 custom validator created using dataannotation showing messages in incorrect place


I am using Asp.net MVC3, razor view engine and data annotation for model validation.

I have a form in which have to input Url details(Url and Description).Both fields are not required. But if i input one field other must be required.If i input description Url is required and is in correct format and if i enter Url then description is required.

I created a customvalidator for data annotation .It validates and output error message. But my problem is error message generated by ValidationMessageFor is in incorrect place. ie,if i enter description ,the required url message, is part of description. I expect that message as part of ValidationMessageFor url field.

Can any one can help me? Thanks in advance. Folowing are the code i used

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]

public sealed class IsExistAttribute : ValidationAttribute, IClientValidatable
{
private const string DefaultErrorMessage = "{0} is required."; 
public string OtherProperty { get; private set; } 
public IsExistAttribute (string otherProperty)
    : base(DefaultErrorMessage)
{
    if (string.IsNullOrEmpty(otherProperty))
    {
        throw new ArgumentNullException("otherProperty");
    }

    OtherProperty = otherProperty;
}

public override string FormatErrorMessage(string name)
{
    return string.Format(ErrorMessageString, name, OtherProperty);
}

protected override ValidationResult IsValid(object value,ValidationContext validationContext)
{
    if (value != null)
    {
        var otherProperty = validationContext.ObjectInstance.GetType()
                                             .GetProperty(OtherProperty);

        var otherPropertyValue = otherProperty
                                    .GetValue(validationContext.ObjectInstance, null);
        var strvalue=Convert.ToString(otherPropertyValue)

        if (string.IsNullOrEmpty(strvalue))
        {
            //return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));

            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName),new[] { OtherProperty});

        }
    }

    return ValidationResult.Success;
 } 

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,ControllerContext context)
{
    var clientValidationRule = new ModelClientValidationRule()
    {
       ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
       ValidationType = "isexist"
    };

   clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty); 
   return new[] { clientValidationRule };           
}      
}

in model

[Display(Name = "Description")]
[IsExist("Url")]
public string Description { get; set; }

[Display(Name = "Url")]
[IsExist("Description")]
[RegularExpression("(http(s)?://)?([\w-]+\.)+[\w-]+(/[\w- ;,./?%&=]*)?", ErrorMessage  = "Invalid Url")]
public string Url { get; set; }

and in view

<div class="editor-field">
@Html.TextBoxFor(m => m.Description )
@Html.ValidationMessageFor(m => m.Description)
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m.Url)
@Html.ValidationMessageFor(m => m.Url)
</div>

unobstrusive validation logic

(function ($) {
    $.validator.addMethod("isexist", function (value, element, params) {
        if (!this.optional(element)) {
            var otherProp = $('#' + params)
            return (otherProp.val() !='' &&  value!='');//validation logic--edited by Rajesh 
        }
        return true;
    });
    $.validator.unobtrusive.adapters.addSingleVal("isexist", "otherproperty");                        
} (jQuery));  

Solution

  • You should make the validation the other way round. Change:

    if (string.IsNullOrEmpty(otherPropertyValue))
        {
            //return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName),new[] { OtherProperty});
    
        }
    

    To:

    if (string.IsNullOrEmpty(Convert.ToString(value)) && !string.IsNullOrEmpty(otherPropertyValue))
        {
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }
    

    And remove if (value != null)

    The field that will then get invalidated is the empty one, in the case the other one is filled.