Validate 1 field (unobtrusive) when other field changes, and check only 1 rule
Asked Answered
S

1

5

Is there any way to trigger a specific jquery-unobtrusive rule on one field, when the value in another field changes?

I have a form with two date fields (say start/end) on it, with validation that end must be greater than start. This works fine in the simple case of changing end after start is already set. What it doesn't do is allow for is:

  • setting end first and then setting start
  • changing start after both have already been set, and violating the constraint

Server side validation will catch it of course, but it looks bad to have the error on end stay set even after you have fixed start, or no error showing up when the value in end is changed to an invalid value. The reason to trigger a specific rule is that I don't want to fire off the required or date format rules on the same field before the user has had a chance to enter a value. The form starts off 'clean'. However, if that isn't possible then firing all the rules is ok.

Sorry for no code samples, but I don't even know where to start with this.

Update:

What i've done for the moment is to dig around in the model (since this is an asp.net mvc project), find the attribute, and read it's properties directly.

var controllerCtx = ViewContext.Controller.ControllerContext;
var da = ViewData.ModelMetadata.GetValidators(controllerCtx)
            .SelectMany(x => x.GetClientValidationRules())
            .Where(x => x.ValidationType == "isdateafter")
            .FirstOrDefault();

var otherid = da == null ? "" : da.ValidationParameters["propertytested"];

Then in the normal HTML part, I do a test on the start and see if it is a date picker, then wire up a basic check, and fire off all validation rules. Since there aren't many rules, I just check to see if there is a value in the end field before running them. I'd like to use the ingenious solution below, and will give it a go when I have a bit of free time this week.

@if (otherid != "") {
    <text>
    var other = $("#@otherid");
    if (other && other.hasClass('hasDatepicker')) { // if the other box is a date/time picker
       other.datetimepicker('option', 'onSelect', function(dateText, instance) {
           var lowerTime = $(this).datetimepicker('getDate');
           $("#@id").datetimepicker('option', 'minDate', new Date(lowerTime.getTime()));
           if ($("#@id").val()) { // if there is a value in the other
                $('form').data('validator').element('#@id');
           }
        });
    }
    </text>
}
Sinner answered 16/5, 2012 at 14:38 Comment(0)
I
10

This might work for you...

$('form').data('validator').element('#Key')

This grabs the validator off of your form, and forces validation on an individual item.

http://docs.jquery.com/Plugins/Validation/Validator/element#element

UPDATE

See if this continues to help!

$.extend($.validator.prototype, {
        elementWithRule: function(element, rule) {
            element = this.clean(element);
            this.lastElement = element;
            this.prepareElement(element);
            this.currentElements = $(element);
            var result = this.checkSpecificRule(element, rule);
            if (result) {
                delete this.invalid[element.name];
            } else {
                this.invalid[element.name] = true;
            }
            if (!this.numberOfInvalids()) {
                // Hide error containers on last error
                this.toHide = this.toHide.add(this.containers);
            }
            this.showErrors();
            return result;
        },
        checkSpecificRule: function(element, rule) {
            element = this.clean(element);

            // if radio/checkbox, validate first element in group instead
            if (this.checkable(element)) {
                element = this.findByName(element.name).not(this.settings.ignore)[0];
            }

            var findRule = { },
                checkRule = $(element).rules()[ rule ];
            var rules;

            if (checkRule) {
                findRule[rule] = checkRule;
                rules = findRule;
            }

            if (!rules) {
                return;                
            }
            var dependencyMismatch = false;
            for (var method in rules) {
                var rule = { method: method, parameters: rules[method] };
                try {
                    var result = $.validator.methods[method].call(this, element.value.replace( /\r/g , ""), element, rule.parameters);

                    // if a method indicates that the field is optional and therefore valid,
                    // don't mark it as valid when there are no other rules
                    if (result == "dependency-mismatch") {
                        dependencyMismatch = true;
                        continue;
                    }
                    dependencyMismatch = false;

                    if (result == "pending") {
                        this.toHide = this.toHide.not(this.errorsFor(element));
                        return;
                    }

                    if (!result) {
                        this.formatAndAdd(element, rule);
                        return false;
                    }
                } catch(e) {
                    this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
                        + ", check the '" + rule.method + "' method", e);
                    throw e;
                }
            }
            if (dependencyMismatch)
                return;
            if (this.objectLength(rules))
                this.successList.push(element);
            return true;
        }
    });

// Then use it like this...
$('form').data('validator').elementWithRule('#Key', 'required');

There didn't appear to be any built in way to do this, so I just hacked something together! :)

Insurrectionary answered 16/5, 2012 at 15:36 Comment(4)
Comes close. It validates the enty, but not one specific rule. I'm still digging in the sparse documentation to find a way to do this, to identify and trigger an individual rule. Thanks though, i'm closer now than I was.Sinner
That is just amazing. One day i'll get jquery internals like this. I haven't had a chance to try it, what I ended up going with (since i'm in a .cshtml file), is a roundabout way to see if my model field has the appropriate validator on it, and then dig out the "other-field-to-validate" option. This is a much better general purpose solution. Sorry I haven't had a chance to try it out yetSinner
No problem! I just did a little bit of digging to figure out how it works on a group of rules then took that code out and manipulated it until it just did the one field. Let me know if you get a chance to try it!Insurrectionary
Worked perfectly! Wow :) Helps me understand the internals a bit more, too. I added in some debugging to verify that the rule was called, the right one executed with the right values, and it's just perfect. Thank you so much. I don't like changing the min-date because that just changes the field's value behind the user's back.Sinner

© 2022 - 2024 — McMap. All rights reserved.