How to set $jQval.unobtrusive.options ErrorPlacement function correclty
Asked Answered
S

3

7

What is the proper way to override the ErrorPlacement function in jquery.validate.unobtrusive?

Looking at the jquery.validate.unobtrusive.js script it appears that the developers' intention would allow you to apply your own ErrorPlacement function by setting $jQval.unobtrusive.options.

In the validationInfo(form) function that defines the errorPlacement function, we see a call to execInContext("errorPlacement", arguments).

It would seem that if we create an errorPlacement function under $.validator.unobtrusive.options, then this will be called.

$.validator.unobtrusive.options = {
    errorPlacement: function () {
        console.log("hello");
    }
};

The issue is that this must configured after jquery.validate.js and before jquery.validate.unobtrusive.js is referenced. Otherwise $jQval.unobtrusive.options is null and $form.data(data_validation, result) will not be set again.

function validationInfo(form) {
    var $form = $(form),
        result = $form.data(data_validation),
        onResetProxy = $.proxy(onReset, form),
        defaultOptions = $jQval.unobtrusive.options || {},
        execInContext = function (name, args) {
            var func = defaultOptions[name];
            func && $.isFunction(func) && func.apply(form, args);
        }

    if (!result) {
        result = {
            options: {  // options structure passed to jQuery Validate's validate() method
                errorClass: defaultOptions.errorClass || "input-validation-error",
                errorElement: defaultOptions.errorElement || "span",
                errorPlacement: function () {
                    onError.apply(form, arguments);
                    execInContext("errorPlacement", arguments);
                },
                invalidHandler: function () {
                    onErrors.apply(form, arguments);
                    execInContext("invalidHandler", arguments);
                },
                messages: {},
                rules: {},
                success: function () {
                    onSuccess.apply(form, arguments);
                    execInContext("success", arguments);
                }
            },
            attachValidation: function () {
                $form
                    .off("reset." + data_validation, onResetProxy)
                    .on("reset." + data_validation, onResetProxy)
                    .validate(this.options);
            },
            validate: function () {  // a validation function that is called by unobtrusive Ajax
                $form.validate();
                return $form.valid();
            }
        };
        $form.data(data_validation, result);
    }

    return result;
}

The other way to get deal with this is to:

  1. set the $.validator.unobtrusive.options errorPlacement function

  2. remove the unobtrusive validation

  3. re-apply the unobtrusive validation

$.validator.unobtrusive.options = {
    errorPlacement: function () {
        console.log("errorPlacement");
    }
};

$("#form_selector").removeData("unobtrusiveValidation");

// reapply the form's validator
$.validator.unobtrusive.parse(document);

Or the other option would be to override the function call.

var validator = $("#form_selector").data('validator');
var originalFunction = validator.settings.errorPlacement;
validator.settings.errorPlacement = function(error,element) {
    console.log("errorPlacement");
    originalFunction(error, element);
};

Which is the proper way of implementing your own errorPlacement method?

Senator answered 18/8, 2014 at 2:51 Comment(2)
Don't suppose you've solved this (or even remember it)?Scarabaeoid
I can't remember now. At the end of my post I suggest two options to get around the issue. I think I may have done the override the function call. It seems cleaner to me.Senator
O
7

3 years late to answer, but turns out I needed exactly this today. It's possible the unobtrusive code has been fixed in that timeframe, but your example code worked just fine for me. I added this to one of my site scripts that was loaded after both jquery.validate.js and jquery.validate.unobtrusive.js:

$.validator.unobtrusive.options = {
    errorPlacement: function () {
        console.log("hello");
    }
};

It worked fine, but with one additional catch: this line has to be ran before document ready. The validationInfo method is called on document ready via this, from the bottom of jquery.validate.unobtrusive.js:

$(function () {
    $jQval.unobtrusive.parse(document);
}); 

parse ultimately calls validationInfo. The problem with the unobtrusive code is when validationInfo sets defaultOptions = $jQval.unobtrusive.options || {}. Clearly if you haven't defined the unobtrusive.options by this point, it'll stupidly set defaultOptions to a blank object and never check it again, regardless of what you do to $jQval.unobtrusive.options. If they just removed the || {} everything would work great, since it's just an object reference. Alas...


If you can't set the errorPlacement callback before your document is loaded, or you need to alter it dynamically, just add this one line in one of your scripts that loads after jquery.validate.js and jquery.validate.unobtrusive.js:

$.validator.unobtrusive.options = {};

That will cause defaultOptions to point to an object you can reference. Then, wherever you need, alter $.validator.unobtrusive.options as you'd expect. Just be careful not to overwrite the object since you wouldn't affect the object pointed to by defaultOptions if you did. ie, do this:

$.validator.unobtrusive.options.errorPlacement: function () {
    console.log("hello");
};
Ouphe answered 26/10, 2017 at 20:53 Comment(2)
for me the solution was #5487639Forelock
You can also set the unobtrusive options in a jQuery document.ready callback that is defined in code before the unobtrusive validation JS is pulled in, which I think is the most elegant solution. This guarantees that your document.ready callback will be run before unobtrusive validation's document.ready callback (where it reads the options), but it will still run after $.validator.unobtrusive.options has been defined by the unobtrusive validation JS.Unstained
B
1

Using Object.assign

After some experimentation i was able to get it working with the code below (loaded after everything). I Am using a bootstrap based theme so it wants has-error on the form-group container

Object.assign($.validator.unobtrusive,
    {
        options: {
            errorPlacement: function(error, element) {
                const container = $(element).closest(".form-group, .form-group-custom");
                if (container.length) {
                    container.addClass("has-error");
                }
            },
            success: function(error, element) {
                const container = $(element).closest(".form-group, .form-group-custom");
                if (container.length) {
                    container.removeClass("has-error");
                }
            }
        }
    });
Bondmaid answered 31/5, 2019 at 9:13 Comment(0)
C
0

I struggled with this issue several days, I tried almost every possible options. It worked in few cases, but not realiably. Too much conditions should be met.

What worked for me is to bundle "jquery.validate.unobtrusive.min.js" with my script. This worked each time without an exception. The order of bundling might be important, put your script second.

script.js:

var settings = {
    errorPlacement: function errorPlacement(error, element) {
        console.log("hello");
    }
}
$.validator.unobtrusive.options = settings;

bundleconfig.json (for .NET environment. Modify it for you needs)

[
  {
    "outputFileName": "wwwroot/js/jquery.validate.unobtrusive.custom.min.js",
    "inputFiles": [
      "wwwroot/lib/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js",
      "wwwroot/js/script.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]
Cimah answered 21/6, 2019 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.