2

We recently decide to migrate to jquery 1.9.1 and since our project is in ASP.NET MVC 3, it has the jquery.validate and jquery.validate.unobtrusive. Everything worked fine and the default integration between these scripts and MVC 3 validation through property atributes were acting just fine.

However since the update, we also had to update the validation scripts and when we did that, those 'on the fly' added rules stopped working. But what's more bizarre is that it only happens on some properties. On others, through the $(form).validate({//things to validate}) the rules simply apply.

Let me give you some code to explain it better. Here's the ViewModel properties (it's a reduced example):

[DataType(DataType.Password)]
        [Required(ErrorMessage = "*Required Field")]
        [Remote("ValidatePassword", "Validator", AdditionalFields = "User")]
        public string SenhaAnterior { get; set; }

[HiddenInput]
        public int VerificacaoSeguranca { get; set; }

Here's the view

    @using (Html.BeginForm("...", "...", null, FormMethod.Post, new { @id = "formMSenha" }))
        { 
    <div id="container">
    @Html.HiddenFor(model => model.VerificacaoSeguranca, new { @id = "hdSeguranca" })
@Html.ValidationMessageFor(model => model.VerificacaoSeguranca)
    <div>
                    <label>
                        Senha anterior:
                        @Html.PasswordFor(model => model.SenhaAnterior, new { @id = "txtSenhaAnterior" })
                        @Html.ValidationMessageFor(model => model.SenhaAnterior)
                    </label>
                </div>
    </div>
    }

And the script

//Even if try this, it still doesn't work
$('#formMSenha').validate({
                rules: {
                    hdSeguranca: {
                        min: 4
                    }
                },
                messages: {
                    hdSeguranca: 'Your password lacks security. Try harder'
                }
            });

    //Since this is a partial view, i have to parse the new added form so the validation rules apply
    $.validator.unobtrusive.parse($("#formMSenha"));

                //Minimum security level
                $('#hdSeguranca').rules('add', { min: 4, messages: { min: 'Your password lack security. Try adding more numbers, letters ou symbols.'} });

Before the update, the new add rule worked but now it doesn't. It's like the new rule never gets parsed by the validator or as it doesn't recognize it. But what's bizarre is if i do this

('#formMSenha').validate({
                rules: {
                    txtSenhaAnterior: {
                        min: 4
                    }
                },
                messages: {
                    hdSeguranca: 'New test'
                }
            });

$('#txtSenhaAnterior').rules('add', { min: 4, messages: { min: 'Newer teste'} });

The validation applies! I have absolutely no idea why.

EDIT

As suggested by @Sparky, i'm showing the rendered HTML of this PartialView (it's jquery dialog), with some little modifications.

<div id="msenhaContainer">
<form method="post" id="formMSenha" action="..." novalidate="novalidate">        <h2>
            Atualize sua senha para uma senha complexa</h2>
        <div id="campos">
            <input type="hidden" value="0" name="VerificacaoSeguranca" id="hdSeguranca" data-val-required="The VerificacaoSeguranca field is required." data-val-number="The field VerificacaoSeguranca must be a number." data-val="true">
            <div>
                <label>
                    Senha anterior:
                    <input type="password" name="SenhaAnterior" id="txtSenhaAnterior" data-val-required="*Campo obrigat&amp;#243;rio" data-val-remote-url="/Validator/ValidarSenhaAnterior" data-val-remote-additionalfields="*.SenhaAnterior,*.Usuario" data-val-remote="&amp;#39;SenhaAnterior&amp;#39; is invalid." data-val="true" class="input-validation-error">
                    <span data-valmsg-replace="true" data-valmsg-for="SenhaAnterior" class="field-validation-error"><span for="txtSenhaAnterior" class="">*Campo obrigatório</span></span>
                </label>
            </div>
            <div>
                <label>
                    Nova Senha: <input type="password" name="SenhaNova" maxlength="18" id="txtSenhaNova" data-val-required="*Campo obrigat&amp;#243;rio" data-val="true" class="valid">
                    <span data-valmsg-replace="true" data-valmsg-for="SenhaNova" class="field-validation-valid"></span>
                </label>
            </div>
            <div>
                <label>
                    Nova Senha Confirmação: <input type="password" name="SenhaNovaConfirmacao" maxlength="18" id="txtSenhaNovaConf" data-val-required="*Campo obrigat&amp;#243;rio" data-val-equalto-other="*.SenhaNova" data-val-equalto="As senhas n&amp;#227;o s&amp;#227;o as mesmas" data-val="true" class="valid">
                    <span data-valmsg-replace="true" data-valmsg-for="SenhaNovaConfirmacao" class="field-validation-valid"></span>
                </label>
            </div>
            <br>
            <label>
                Força da Senha</label>
            <div id="progressoSenha" class="ui-progressbar ui-widget ui-widget-content ui-corner-all" role="progressbar" aria-valuemin="0" aria-valuemax="6" aria-valuenow="2">
            <div class="ui-progressbar-value ui-widget-header ui-corner-left" style="display: block; width: 33%; background: none repeat scroll 0% 0% rgb(209, 125, 14);"></div></div>
            <span data-valmsg-replace="true" data-valmsg-for="VerificacaoSeguranca" class="field-validation-valid"></span>
            <div style="text-align: center" id="descricaoForcaSenha">Fraca</div>
            <a id="regrasSenha" href="#" title="Tooltip com as regras aqui!">Regras da senha complexa</a>
        </div>
<input type="hidden" name="freebird" data-val-required="If i stay, will still remember me" id="freebird"></form></div>

<script type="text/javascript">
    //Xhalent's function
    (function ($) {
        $.validator.unobtrusive.parseDynamicContent = function (selector) {
            //use the normal unobstrusive.parse method
            $.validator.unobtrusive.parse(selector);

            //get the relevant form
            var form = $(selector).first().closest('form');

            //get the collections of unobstrusive validators, and jquery validators
            //and compare the two
            var unobtrusiveValidation = form.data('unobtrusiveValidation');
            var validator = form.validate();

            $.each(unobtrusiveValidation.options.rules, function (elname, elrules) {
                if (validator.settings.rules[elname] == undefined) {
                    var args = {};
                    $.extend(args, elrules);
                    args.messages = unobtrusiveValidation.options.messages[elname];
                    //edit:use quoted strings for the name selector
                    $("[name='" + elname + "']").rules("add", args);
                } else {
                    $.each(elrules, function (rulename, data) {
                        if (validator.settings.rules[elname][rulename] == undefined) {
                            var args = {};
                            args[rulename] = data;
                            args.messages = unobtrusiveValidation.options.messages[elname][rulename];
                            //edit:use quoted strings for the name selector
                            $("[name='" + elname + "']").rules("add", args);
                        }
                    });
                }
            });
        }
    })($);
    //DOM ready
    $(function () {
    //Since this is a test, there's no reason to submit the form to the server
        $.validator.setDefaults({
            debug: true
        });

        //Trying to remove the validator to revalidate the form with the new added rule
        $("#formMSenha").removeData("validator").removeData("unobtrusiveValidation");

        $.validator.unobtrusive.parse($("#formMSenha"));

        $('#hdSeguranca').rules('add', { min: 4, messages: { min: 'Your password lacks security'} });

        //Got this example on Xhalent's blog, but it didn't work either
        //        $.validator.unobtrusive.parseDynamicContent($('#hdSeguranca'));

        $.validator.unobtrusive.parse($("#formMSenha"));

        $('#progressoSenha').progressbar({ max: 6, value: 0 });

        $('#txtSenhaNova').keyup(function () {
            VerifyPasswordStrength($(this).val());
        });

        $('#regrasSenha').tooltip({ content: function () {
            //            var texto = $('&lt;div style="display: inline" &gt;&lt;/div&gt;');
            //            texto.html('Regras da senha complexa:');
            //            texto.append("&lt;ul&gt;").append('&lt;li&gt;').append(
            //            $('&lt;span&gt;').html('Deve possuir pelo menos 6 caracteres')).insertAfter($('&lt;li&gt;').append('&lt;span&gt;').html('Deve ter ao menos um número')).
            //            insertAfter($('&lt;li&gt;').append('&lt;span&gt;').html('Deve ter ao menos um caractere especial. Ex:"! @ # $ % &amp;" '));
            //            return texto;

            var textoSenha = $('#descSenha').html();
            return textoSenha;
        }
        });

        $('#formMSenha').submit(function () {
            $.ajax({
                type: 'post',
                url: '...',
                data: $('#formMSenha').serialize(),
                dataType: 'json',
                success: function (data) {
                    $.growlUI('Sucesso', 'Sucesso', 'Flawlessly done!');

                    setTimeout(function () { window.location.replace('/') }, 2000);
                }
            });

            return false;
        });

    });                                   //JQUERY END
    function VerifyPasswordStrength(password) {
        //Removed the security code, but what it does is validate the input
        //to check if the user abide to the safety rules
        //When everything is done, it assigns a value to the hidden var
        //The mininum score required is 4. So if the user don't hit 4 points
        //it can't let him proceed and submit the form
        $('#hdSeguranca').val(passwordScore);

        var progressbar = $("#progressoSenha");
        var progressbarValue = progressbar.find(".ui-progressbar-value");
        var cor = '#9c9999';

        switch (judite) {
            case 1:
                cor = '#790f0f'; //red
                break;
            case 2:
                cor = '#d17d0e'; //orange
                break;
            case 3:
                cor = '#e1e40b'; //yellow  
                break;
            case 4:
                cor = '#129f0a'; //green
                break;
            case 5:
                cor = '#0768c7'; //blue
                break;
            case 6:
                cor = '#92dae8'; //brighter blue
                break;
        }

        progressbarValue.css({ "background": '' + cor });

        $('#progressoSenha').progressbar("option", "value", passwordScore);

        $('#descricaoForcaSenha').html(desc[passwordScore]);
    }

</script>
AdrianoRR
  • 1,131
  • 1
  • 17
  • 36

2 Answers2

0

You have not shown your rendered HTML, but it appears like you're only creating and referencing id attributes. The field names that you use inside of .validate() for declaring your rules are supposed to match the name attribute of the input, not the id.

And for this plugin to work properly, all input elements must contain a unique name attribute.

HTML:

<input type="text" name="myfield" id="something" />

jQuery:

$(document).ready(function() {

    $('#myform').validate({
        rules: {
            myfield: { // <-- this is the NAME attribute
                required: true,
                minlength: 5
            }
        }
    });

});
Sparky
  • 98,165
  • 25
  • 199
  • 285
  • I'll try what you're suggesting, but what i found to be strange is the fact thsi validation works on `txtSenhaAnterior` field. My guess is since it already has some default validation rules, adding another is no big deal to the parser – AdrianoRR Apr 16 '13 at 21:07
  • @AdrianoRR, of course it's working on that one field; that's because you've applied the rules using the `rules('add')` method, where the jQuery selector is targeting its `id`. Within `.validate()`, you must use the `name`. However, with `rules('add')`, you can use _any valid_ jQuery selector. `rules('add')` also dynamically _over-rides_ whatever rules you apply within `.validate()`. Understand? – Sparky Apr 16 '13 at 21:45
  • I do, but have your read original post? There says i tried to add the rule through `$('selector').rules('add', {min: 4});` and it also didn't work. And i tried what you suggested but it didn't work either. My other guess is that have something to do with `$.validator.unobtrusive.parse` – AdrianoRR Apr 17 '13 at 17:20
  • @AdrianoRR, I suggest that you show your _rendered_ HTML output in your original post. Since we're talking about JavaScript, rendered HTML is the only thing it can see. – Sparky Apr 17 '13 at 17:24
  • just did it. I guess i'll just add this validation on server side. But it really bugs me that this new version of validate isn't working as supposed. – AdrianoRR Apr 17 '13 at 18:41
  • @AdrianoRR, the jQuery Validate plugin is working exactly as it's supposed. It's a problem somewhere in _your_ code and since that's a lot of code, I strongly recommend that you strip it down into only what's necessary to replicate the problem. One problem I see immediately is that you have a `.submit()` handler for your ajax. Typically, this will bypass validation. If you read the plugin's docs, you'll see that `ajax` belongs inside the plugin's `submitHandler` function. – Sparky Apr 17 '13 at 19:20
  • Got my answer! Just checked the changelog of validate plugin and in it's 1.9 version, hidden field validation was by default ignored! That was the problem all along! Anyway, thanks for your help. When you said that the validate plugin was working correctly i did a different search and found the changelog. And thanks for the tip on the `SubmitHandler` event. – AdrianoRR Apr 17 '13 at 19:55
0

It seems that after version 1.9, validate script by default ignores hidden field validation. So my attempt to add the validation rule was correct, the problem was that it was add to hidden field.

You can check the changelog here: http://jquery.bassistance.de/validate/changelog.txt

But if you need to validate a hidden field, you can change the script ignore with the code below

$.validator.setDefaults({ 
    ignore: [],
    // any other default options and/or rules
});
AdrianoRR
  • 1,131
  • 1
  • 17
  • 36
  • See this answer as well. It might be relevant: http://stackoverflow.com/a/8565769/594235 – Sparky Apr 17 '13 at 19:59
  • Look at that! It seems that you found this problem once. Happened to be just the exact case here. Just give you a +1 since is basically the answer to my own problem – AdrianoRR Apr 17 '13 at 20:08