[RESOLVED]Custom Validator – three phone fields must have a value

I have 3 text boxes, none are required. Now, i need to check that either all are blank or all have a value. I have come up with the following attribute but

1. it will only work with the 3 text boxes, where as i want to make it generic so that i specify multiple properties of the class

2. i would like to display the error right next to the empty text boxes (all) not to the one this attribute is attached. i can apply the attribute to all the properties and provide the error message for only one. The rest will have an empty space for the
message.

3. in case of multiple properties, how can i pass the name of all of these to the client side without separating these with a special characters? I don’t want to turn these into an array on the client side.

4. when i apply the attribute to property inside a class and this class is setup as a property inside a view model, the id passed to the client side for the compare controls gets passed as is. Here is the example

Class student

[PhoneCodePrefixNumber("Prefix", "Number", ErrorMessage=" ")]
public string Code { get; set;}

[PhoneCodePrefixNumber("Code", "Number", ErrorMessage=" ")]
public string Prefix { get; set; }

[PhoneCodePrefixNumber("Code", "Prefix", ErrorMessage="All of the items need to be selected")]
public string Number { get; set; }

View model

public Student Info { get; set; }

Now the id for these on the page is

1. Info_Code
2. Info_Prefix
3. Info_Number

Where as the id getting passed to the client side is without Info_

1. Code
2. Prefix
3. Number

Html from the page:

data-val-phonecodeprefixnumber-fieldone="Code" data-val-phonecodeprefixnumber-fieldtwo="Prefix"

How can i make this attribute to pass proper id to the client side? I don’t want to move the three properties from the student class to the view model

Also in the above example, if i click submit while keeping only Code blank, then the red box appears with the items that have value in them. It doesn’t show with code

And if change the compare to ids to Info_… then the client side validates but then server side has an issue and it throws a null exception when i try to get the valFieldOne since baseFieldOneInfo is null.

Attribute code

public sealed class PhoneCodePrefixNumberAttribute : ValidationAttribute, IClientValidatable
    {
        public string FieldOne { get; set; }
        public string FieldTwo { get; set; }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var valThis = (IComparable)value;

            var baseFieldOneInfo = validationContext.ObjectType.GetProperty(FieldOne);
            var valFieldOne = (IComparable)baseFieldOneInfo.GetValue(validationContext.ObjectInstance, null);

            var baseFieldTwoInfo = validationContext.ObjectType.GetProperty(FieldOne);
            var valFieldTwo = (IComparable)baseFieldTwoInfo.GetValue(validationContext.ObjectInstance, null);

            ValidationResult result = null;

            if (valThis != null || valFieldOne != null || valFieldTwo != null)
            {
                if (
                    (valThis != null && (valFieldOne == null || valFieldTwo == null)) ||
                    (valFieldOne != null && (valThis == null || valFieldTwo == null)) ||
                    (valFieldTwo != null && (valThis == null || valFieldOne == null))
                    )
                {
                    result = new ValidationResult(base.ErrorMessage);
                }
            }
            return result;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
            ControllerContext context)
        {
            var errorMessage = this.FormatErrorMessage(metadata.DisplayName);
            var compareRule = new ModelClientValidationRule();
            compareRule.ErrorMessage = errorMessage;
            compareRule.ValidationType = "phonecodeprefixnumber";
            compareRule.ValidationParameters.Add("fieldone", FieldOne);
            compareRule.ValidationParameters.Add("fieldtwo", FieldTwo);
            yield return compareRule;
        }
    }

Client side validation

//PhoneCodePrefixNumber Client Validation Rules
            $.validator.addMethod("phonecodeprefixnumber", function (value, element, params) {
                // debugger;
                var fieldone = params.split(",")[0];
                var fieldtwo = params.split(",")[1];
                
                if (params == undefined || params == null || params.length == 0 ||
                    value == undefined || value == null || value.length == 0 ||
                    fieldone == undefined || fieldone == null || fieldone.length == 0 ||
                    fieldtwo == undefined || fieldtwo == null || fieldtwo.length == 0){
                    return true;
                }
                
                var valueFieldOne = $("#"+fieldone).val();
                var valueFieldTwo = $("#"+fieldtwo).val();
                alert(value + "|" + fieldone + "=" + valueFieldOne + "|" + fieldtwo + "=" + valueFieldTwo);

                if (value == "" && valueFieldOne == "" && valueFieldTwo == "")
                    return true;

                if (value == "" || valueFieldOne == "" || valueFieldTwo == "")
                    return false;

                return true;
            });
            $.validator.unobtrusive.adapters.add("phonecodeprefixnumber", ["fieldone", "fieldtwo"], function (options) {
                options.rules["phonecodeprefixnumber"] = options.params.fieldone + "," + options.params.fieldtwo;
                options.messages["phonecodeprefixnumber"] = options.message;
            });

Thanks for looking!

try this,

this way you can specify as many as fields as want with a comma separated string

public class contact
    {
        //[PhoneCodePrefixNumber("Prefix", "Number", ErrorMessage = " ")]
        public string Code { get; set; }

        //[PhoneCodePrefixNumber("Code", "Number", ErrorMessage = " ")]
        public string Prefix { get; set; }

        [PhoneCodePrefixNumber(Fields="Code,Prefix", ErrorMessage = "All of the items need to be selected")]
        public string Number { get; set; }
    }
    public sealed class PhoneCodePrefixNumberAttribute : ValidationAttribute, IClientValidatable
    {
        public string Fields { get; set; }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var valThis = (IComparable)value;
            bool hasValue = (valThis != null);

            ValidationResult result = ValidationResult.Success;


            foreach (string fldName in Fields.Split(",".ToCharArray()))
            {
                var basefldInfo = validationContext.ObjectType.GetProperty(fldName);
                var valfld = (IComparable)basefldInfo.GetValue(validationContext.ObjectInstance, null);

                if (hasValue != (valfld != null))
                    result = new ValidationResult(base.ErrorMessage);
            }
            return result;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
            ControllerContext context)
        {
            var errorMessage = this.FormatErrorMessage(metadata.DisplayName);
            var compareRule = new ModelClientValidationRule();
            compareRule.ErrorMessage = errorMessage;
            compareRule.ValidationType = "phonecodeprefixnumber";
            compareRule.ValidationParameters.Add("fields", Fields);
            yield return compareRule;
        }
    }
<script>
    function hasValue(field) {
        var fldObject = $('#' + field);
        return (!(fldObject.length == 0 || fldObject.val() == ''));
    }
    //PhoneCodePrefixNumber Client Validation Rules
    $.validator.addMethod("phonecodeprefixnumber", function (value, element, params) {
        //debugger
        var fields = params.split(",");

        if (fields.length == 0)
            return true;

        var ValueStatus = (!(value == undefined || value == null || value.length == 0));

        for (i = 0; i < fields.length; i++) {
            if (ValueStatus != hasValue(fields[i]))
                return false;
        }

        return true;
    });
    $.validator.unobtrusive.adapters.add("phonecodeprefixnumber", ["fields"], function (options) {
        options.rules["phonecodeprefixnumber"] = options.params.fields;
        options.messages["phonecodeprefixnumber"] = options.message;
    });
</script>

Learning…

Now the id for these on the page is

1. Info_Code
2. Info_Prefix
3. Info_Number

Where as the id getting passed to the client side is without Info_

1. Code
2. Prefix
3. Number

<script>
    function hasValue(field) {
        var fldObject = $('#' + field);
        return (!(fldObject.length == 0 || fldObject.val() == ''));
    }
    //PhoneCodePrefixNumber Client Validation Rules
    $.validator.addMethod("phonecodeprefixnumber", function (value, element, params) {
        debugger

        var baseIDName = "";
        var UsIndex= element.id.lastIndexOf("_");
        if (UsIndex >= 0)
            baseIDName = element.id.substring(0, UsIndex + 1);
        var fields = params.split(",");
         
        if (fields.length == 0)
            return true;
        var validationresult=true;
        var ValueStatus = (!(value == undefined || value == null || value.length == 0));

        for (i = 0; i < fields.length; i++) {
            if (ValueStatus != hasValue(baseIDName+fields[i]))
                validationresult = false;
        }
            if(!validationresult)
            {
                for (j = 0; j < fields.length; j++) {
                    var span = $('#' + baseIDName+ fields[j]).next();
                    span.removeClass("field-validation-valid");
                    span.addClass("field-validation-invalid");
                    span.text($(element).next().text());

                }
            }        
        return validationresult;
    });
    $.validator.unobtrusive.adapters.add("phonecodeprefixnumber", ["fields"], function (options) {
        options.rules["phonecodeprefixnumber"] = options.params.fields;
        options.messages["phonecodeprefixnumber"] = options.message;
    });
</script>

@cnuonline thanks for the code. I’ll test all this on monday and will get back to you.

Leave a Reply