-1

I'm wrote a custom ValidationAttribute that also implements IClientValidatable so that I can perform client-side validation using the Jquery.Unobtrusive library.

The issue I have is that even though I have multiple items in the EditLeadViewMode.AssociatedUsers collection, the GetClientValidationRules method only fires once, for the first item in the collectio and only the first item on the form is validated on the client. I can't figure out why it won't fire/validate for all of the items in my collection.

Sample HTML from the form. You can see that the data-val-requiredassignedto attribute is not set on the second box

<select id="AssociatedUsers[1].UserId" name="AssociatedUsers[1].UserId" class="form-control valid" data-val-requiredassignedto="ErrorMessage" aria-describedby="AssociatedUsers[1].UserId-error" aria-invalid="false"><option value="">-- Assign To --</option>
<option selected="selected" value="2">A.Carlie Predovic</option>
<option value="4">A.Earline Pfeffer</option>
<option value="10">F.Dorris Dare</option>
<option value="7">I.Alexandrea Hane</option>
<option value="8">I.Alexandrea Rogahn</option>
<option value="6">I.Heber Greenfelder</option>
<option value="9">I.Maeve Koepp</option>
</select>


<select id="AssociatedUsers[2].UserId" name="AssociatedUsers[2].UserId" class="form-control" ><option value="">-- Assign To --</option>
<option value="2">A.Carlie Predovic</option>
<option value="4">A.Earline Pfeffer</option>
<option value="5">A.Jarrod Breitenberg</option>
<option selected="selected" value="3">A.Sheridan Maggio</option>
<option value="10">F.Dorris Dare</option>
</select>

ViewModel bound to the view

public sealed class EditLeadViewModel : IHelperSetup
    {
        public EditLeadViewModel()
        {
        }

        public IEnumerable<UserInLeadRole> AssociatedUsers { get; set; }
    }

public class UserInLeadRole
    {
        [AssignToUserValidationAttribute]
        public int? UserId { get; set; }
    }

Custom Attribute

 public class AssignToUserValidationAttribute : ValidationAttribute, IClientValidatable
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            //Server side validation here
        }
        
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            return new List<ModelClientValidationRule>
            { 
                new ModelClientValidationRule{
                    ValidationType = "requiredassignedto",
                    ErrorMessage = "This field is required"
                }
            };

        }
    }

Javascript for wiring up my custom unobtrusive adapter

 function addRule(name, params, ruleFn)
    {
        $.validator.addMethod(name, ruleFn);
        $.validator.unobtrusive.adapters.add(name, params,
            function (options)
            {
                options.rules[name] = options.params;
                options.messages[name] = options.message;
            });
    }
   
    addRule("requiredassignedto",[],
        function (value, el, params) {
            ///Client side validation logic here
        });

})(jQuery);

I can't find any documentation saying that a custom validator will only fire once on a collection, but I'm obviously missing something.

Agreene
  • 508
  • 7
  • 16

1 Answers1

0

I suspect your addRule is only working on the first item.


I would recommend a couple of things, first simplify it with a direct validation attribute on the ViewModel

For e.g.

public class UserInLeadRole
{
    [DisplayName("Tesing- User ID:")]
    [Required(ErrorMessage = "Testing -- Please enter user ID or something...")]    
    public int? UserId { get; set; }
}

Next I would check/debug if your addRule is infact getting added to all the collection items. Setup a break point inside that and see if all the items in the collection are invoking the addRule.

Transformer
  • 6,963
  • 2
  • 26
  • 52
  • I need a custom attribute because I have to tailor the error message depending on the value of another field, called RoleName, that I left out of UserInLeadRole for simplicity's sake when creating this question. So the error message has to say something like "{Value of UserInLeadRole.RoleName} is a required field. Is there another way to accomplish this other than a custom attribute? Also, I put a breakpoint in GetClientValidationRules and it's only called once even though there are 5 items in the IEnumerable AssociatedUsers collection – Agreene Jul 28 '21 at 01:19
  • 1
    Sure you can come back to your `custom attribute`, but `first lets zero-in on the culprit`, lets _make it simple and then you can add this custom attribute back_ once we locate the issue. Also, from your comment, as I suspected its only getting called/added to your first element. You need to debug your addRule and `verify that the rule is set on the collection` **not just** the `first element`. Can you confirm this is where you iterate and setup the validation for the collection. – Transformer Jul 28 '21 at 01:26
  • 1
    Check out this ref. https://blog.craigtp.co.uk/Post/2017/01/05/Complex_custom_validation_with_ASP.NET_MVC,_jQuery_and_KnockoutJS & this https://stackoverflow.com/a/46159269/6085193 , https://ml-software.ch/posts/extending-client-side-validation-with-fluentvalidation-and-jquery-unobtrusive-in-an-asp-net-core-application – Transformer Jul 28 '21 at 01:32
  • Yes. I can confirm that the rule is only set on the first element in the collection. – Agreene Jul 30 '21 at 16:31