web-dev-qa-db-fra.com

jquery.validate.unobtrusive ne fonctionne pas avec les éléments injectés dynamiques

Je travaille avec ASP.Net MVC3, Le moyen le plus simple d'utiliser la validation du client serait d'activer le jquery.validate.unobtrusive. Tout fonctionne bien, pour les choses qui viennent du serveur.

Mais lorsque j'essaie d'injecter du javascript dans de nouvelles "entrées", je savais que je devais appeler $.validator.unobtrusive.parse() pour relier les validations. Mais tout de même, tous ces champs injectés dynamiques ne fonctionnent pas.

Pire encore, j'essaie de lier manuellement en utilisant jquery.validate Et cela ne fonctionne pas non plus. Des pensées?

98
xandy

J'ai créé une extension pour la bibliothèque jquery.validate.unobtrusive qui a résolu ce problème dans mon cas - cela pourrait être intéressant.

http://xhalent.wordpress.com/2011/01/24/applying-unobtrusive-validation-to-dynamic-content/

66
Xhalent

J'ai essayé l'approche de Xhalent mais malheureusement cela ne fonctionnait pas pour moi. L'approche de Robin a fonctionné et n'a pas fonctionné. Cela fonctionnait très bien pour les éléments ajoutés dynamiquement, mais si vous tentiez d'utiliser JQuery pour supprimer tous les attributs de validation et les étendues du DOM, la bibliothèque de validation tenterait toujours de les valider.

Toutefois, si vous supprimez les données "unobtrusiveValidation" du formulaire en plus de "validationData", cela fonctionnait comme un charme pour ajouter et supprimer de manière dynamique des éléments que vous souhaitez valider ou non.

$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
156
viggity

En fait, j'aime beaucoup la simplicité de la solution @viggity et @Robins, alors je l'ai transformée en un petit plugin rapide:

(function ($) {

    $.fn.updateValidation = function () {
        var $this = $(this);
        var form = $this.closest("form")
            .removeData("validator")
            .removeData("unobtrusiveValidation");

        $.validator.unobtrusive.parse(form);

        return $this;
    };
})(jQuery);

Exemple d'utilisation:

$("#DischargeOutcomeNumberOfVisits")
    .attr("data-val-range-min", this.checked ? "1" : "2")
    .updateValidation();
41
lambinator

J'ai le même problème. J'ai découvert qu'il n'est pas possible d'appeler deux fois $ .validator.unobtrusive.parse () sur le même formulaire. Lors du chargement initial du formulaire à partir du serveur, celui-ci est analysé automatiquement par la bibliothèque discrète. Lorsque vous ajoutez dynamiquement un élément d'entrée au formulaire et appelez à nouveau $ .validator.unobtrusive.parse (), cela ne fonctionnera pas. La même chose vaut pour parseElement ().

La lib discrète appelle la méthode validate du plugin jquery validate pour définir toutes les règles et tous les messages. Le problème est que, lorsqu'il est rappelé, le plug-in ne met pas à jour le nouvel ensemble de règles qui lui est attribué.

J'ai trouvé une solution brute: avant d'appeler la méthode parse sur la lib sans faille, je jette le validateur de formulaire:

$('yourForm').removeData("validator");

Désormais, lorsque la méthode validate est appelée par la bibliothèque non intrusive, toutes les règles et tous les messages sont recréés, y compris les entrées ajoutées dynamiquement.

J'espère que cela t'aides

32
Robin van der Knaap

J'utilise MVC 4 et JQuery 1.8, on dirait que le code suivant est nécessaire pour permettre à Jquery de valider le contenu injecté de manière dynamique via Ajax, ou Jquery dans le DOM.

J'ai créé une fonction modulaire qui accepte l'objet Jquery du nouvel élément ajouté. Si vous avez cloné une nouvelle table avec l'id tblContacts en utilisant Jquery en cliquant sur un bouton, incluez la fonction ci-dessous dans votre fichier js

function fnValidateDynamicContent(element) {
    var currForm = element.closest("form");
    currForm.removeData("validator");
    currForm.removeData("unobtrusiveValidation");
    $.validator.unobtrusive.parse(currForm);
    currForm.validate(); // This line is important and added for client side validation to trigger, without this it didn't fire client side errors.
}

et appelez ça comme ça:

fnValidateDynamicContent("#tblContacts")
8
Sundara Prabu

En prenant la solution de Xhalent marquée comme réponse ci-dessus, je l'ai développée un peu.

$.validator.unobtrusive.parseDynamicContent = function (selector) {
    var $selector = $(selector),
        $jqValUnob = $.validator.unobtrusive,
        selectorsDataValAttr = $selector.attr('data-val'),
        $validationInputs = $selector.find(':input[data-val=true]');

    if ((selectorsDataValAttr !== 'true') && 
        ($validationInputs.length === 0)) { 
        return; 
    }

    if (selectorsDataValAttr === 'true') {
        $jqValUnob.parseElement(selector, true);
    }

    $validationInputs.each(function () {
        $jqValUnob.parseElement(this, true);
    });

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

    $jqValUnob.syncValdators($form);
};

/* synchronizes the unobtrusive validation with jquery validator */
$.validator.unobtrusive.syncValdators = function ($form) {
    if ($.hasData($form[0])) {
        var unobtrusiveValidation = $form.data('unobtrusiveValidation'),
            validator = $form.validate();

        // add validation rules from unobtrusive to jquery
        $.each(unobtrusiveValidation.options.rules, function (elname, elrules) {
            if (validator.settings.rules[elname] == undefined) {
                var args = {};
                $.extend(args, elrules);
                args.messages = unobtrusiveValidation.options.messages[elname];
                $("[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];
                        $("[name='" + elname + "']").rules("add", args);
                    }
                });
            }
        });
        // remove all validation rules from jquery that arn't in unobtrusive
        $.each(validator.settings.rules, function (elname, elrules) {
            if (unobtrusiveValidation.options.rules[elname] === undefined) {
                delete validator.settings.rules[elname];
            } else {
                $.each(elrules, function (rulename, data) {
                    if (rulename !== "messages" && unobtrusiveValidation.options.rules[elname][rulename] === undefined) {
                        delete validator.settings.rules[elname][rulename];
                    }
                });
            }
        });
    }        
};

$.validator.unobtrusive.unparseContent = function (selector) {
    var $selector = $(selector);

    // if its  a text node, then exit
    if ($selector && $selector.length > 0 && $selector[0].nodeType === 3) {
        return;
    }

    var $form = $selector.first().closest('form'), 
        unobtrusiveValidation = $form.data('unobtrusiveValidation');

    $selector.find(":input[data-val=true]").each(function () {
        removeValidation($(this), unobtrusiveValidation);
    });
    if ($selector.attr('data-val') === 'true') {
        removeValidation($selector, unobtrusiveValidation);
    }
    $.validator.unobtrusive.syncValdators($form);
};

function removeValidation($element, unobtrusiveValidation) {
    var elname = $element.attr('name');
    if (elname !== undefined) {
        $element.rules('remove');
        if (unobtrusiveValidation) {
            if (unobtrusiveValidation.options.rules[elname]) {
                delete unobtrusiveValidation.options.rules[elname];
            }
            if (unobtrusiveValidation.options.messages[elname]) {
                delete unobtrusiveValidation.options.messages[elname];
            }
        }
    }
}

Donc, fondamentalement, cela fonctionne toujours de la même manière que la solution de Xhalent ci-dessus, mais j'ai ajouté la possibilité de supprimer les règles pour les éléments que vous supprimez du domaine. Ainsi, lorsque vous supprimez des éléments du Dom et que vous souhaitez également supprimer ces règles de validation, appelez:

$.validator.unobtrusive.unparseContent('input.something');
2
Evan Larsen

Pourquoi ne pas utiliser la fonction rules directement à partir du document de validation jquery. Comme ça:

$('#newField0').rules('add', {
    required: true,
    minlength: 2
});
//use Html.ValidationMessage will renders a span element, unobtrusive need it to display errors
$('@Html.ValidationMessage("newField0")').insertAfter('#newField0');
2
zhangfx

J'ai essayé la réponse de viggity et au début, tout semblait marcher. Mais au bout d’un moment, j’ai remarqué que la validation ralentissait péniblement les éléments les plus dynamiques que j’avais ajoutés. La raison en était que sa solution ne déliait pas les gestionnaires d'événements, mais en ajoutait de nouveaux à chaque fois. Donc, si vous ajoutez 5 éléments, la validation est exécutée 6 fois au lieu d’une seule fois. Pour résoudre ce problème, vous devez dissocier les événements en plus des appels removeData.

$("form").removeData("validator")
         .removeData("unobtrusiveValidation")
         .off("submit.validate click.validate focusin.validate focusout.validate keyup.validate invalid-form.validate");
$.validator.unobtrusive.parse("form");
1
Benedikt Langer

En cas de contenu dynamique, vous devez mettre à jour les validations non restrictives comme indiqué ci-dessous et vérifier si Form est valide lors de la soumission.

function UpdateUnobtrusiveValidations(idForm) {
    $(idForm).removeData("validator").removeData("unobtrusiveValidation");
    $.validator.unobtrusive.parse($(idForm));
};


$('#idDivPage').on('click', '#idSave', function (e) {
    e.preventDefault();
    if (!$('#idForm').valid()) {
        // Form is invalid … so return
        return false;
    }
    else {
        // post data to server using ajax call
        // update Unobtrusive Validations again
        UpdateUnobtrusiveValidations('#idForm');
    }
});
1
Jagdeesh Motegowda

J'ai trouvé le script de code de @ Xhalent dans mon code et j'allais le supprimer car je ne l'utilisais pas, ce qui m'a amené à cette SO question.

Ce code est assez propre et simple:

jQuery.fn.unobtrusiveValidationForceBind = function () {
    //If you try to parse a form that is already parsed it won't update
    var $form = this
       .removeData("validator") /* added by the raw jquery.validate plugin */
            .removeData("unobtrusiveValidation");  /* added by the jquery     unobtrusive plugin */

    $form.bindUnobtrusiveValidation();
}

Ensuite, pour appeler cette extension jQuery, utilisez simplement un sélecteur pour récupérer votre formulaire:

$('#formStart').unobtrusiveValidationForceBind();

Alto!

1
Brian Ogden

Tout d'abord, je pense que l'appel devrait être .validator, pas valider, alors vous devez passer l'id du formulaire

$.validator.unobtrusive.parse("#id");
0
Chris Allmark

Cela fait un moment que je bricole avec cela, j’ai abandonné des solutions et réessayé plus tard (quand j’ai du temps libre, croyez-le ou non).

Je ne sais pas si ce comportement aurait changé dans les versions plus récentes de jquery (nous utilisons 1.7.2) depuis la création ou la dernière observation de ce fil de discussion, mais j'ai constaté que .parseElement(inputElement) fonctionnait correctement lorsque j'essayais de ajouter des éléments créés dynamiquement à un formulaire sur lequel un validateur est déjà chargé. Cela avait déjà été suggéré par @jamesfm (15 février 2011) dans l'un des commentaires ci-dessus, mais je l'ai oublié les premières fois où j'ai travaillé sur ce sujet. J'ajoute donc cette réponse séparément pour la rendre plus claire et parce que je pense que c'est une bonne solution et qu'elle ne nécessite pas autant de frais généraux. Cela pourrait ne pas être pertinent pour toutes les questions soulevées dans les réponses ultérieures, mais je pense que ce serait une solution à la question initiale. Voici comment j'ai fait fonctionner le mien:

//row & textarea created dynamically by an async ajax call further up in the code
var row = ...; //row reference from somewhere
var textarea = row.find("textarea");
textarea.val("[someValue]");

//add max length rule to the text area
textarea.rules('add', {
    maxlength: 2000
});
//parse the element to enable the validation on the dynamically added element
$.validator.unobtrusive.parseElement(textarea);
0
Ben