jquery.validate.unobtrusive não funciona com elementos dinâmicos injetados

99

Estou trabalhando com ASP.Net MVC3, a maneira mais fácil de usar a validação do cliente seria habilitando o jquery.validate.unobtrusive. Tudo funciona bem, para coisas que vêm diretamente do servidor.

Mas quando tento injetar algumas novas 'entradas' com javascript, e soube que preciso chamar $.validator.unobtrusive.parse()para religar as validações. Mesmo assim, todos aqueles campos injetados dinâmicos não estão funcionando.

Pior ainda, tento ligar manualmente usando jquery.validatee também não está funcionando. Alguma ideia?

xandy
fonte
1
Se você veio aqui do google, provavelmente está procurando a resposta de @Sundara Prabu, pois as outras respostas com votos favoráveis ​​são antigas.
The Muffin Man

Respostas:

65

Eu criei uma extensão para a biblioteca jquery.validate.unobtrusive que resolveu esse problema para minha situação - pode ser do meu interesse.

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

Xhalent
fonte
Acho que devo votar sua resposta como a certa. BTW, a sua biblioteca funciona com o MVC RC mais recente? ou eles apenas consertaram isso?
xandy
1
Ao usar o código vinculado acima, certifique-se de atualizá-lo para incluir o código nos comentários, caso contrário, ele pode falhar. Especificamente, altere as duas linhas do seletor para aspas duplas do id.
Ryan O'Neill
1
Finalmente consegui fazer com que funcionasse no meu cenário após alguma depuração. Parece que não deve passar um seletor de entrada, mas o pai da entrada ou o formulário, devido à chamada para $.validator.unobtrusive.parse(). Além disso, parece apenas adicionar novas regras, mas não seleciona atualizações para regras existentes.
lambinator
5
Essa resposta deve conter pelo menos os ossos de urso da postagem do blog. As respostas apenas para links geralmente estão fora do tópico para SO
Liam,
8
Embora esta resposta tenha mais de 3 anos, seria extremamente útil se você pudesse postar as partes essenciais da resposta aqui, neste site, ou sua postagem corre o risco de ser excluída. Veja o FAQ onde menciona respostas que são "pouco mais que um link '. Você ainda pode incluir o link se desejar, mas apenas como uma 'referência'. A resposta deve ser independente, sem precisar do link.
Taryn
159

Tentei a abordagem de Xhalent, mas infelizmente não funcionou para mim. A abordagem de Robin funcionou e não funcionou. Funcionou muito bem para elementos adicionados dinamicamente, mas se você tentasse usar JQuery para remover todos os atributos de validação e spans do DOM, a biblioteca de validação ainda tentaria validá-los.

No entanto, se você remover os dados "unobtrusiveValidation" do formulário além de "validationData", funcionará perfeitamente para adicionar e remover dinamicamente os elementos que você deseja validar ou não validar.

$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
viggidade
fonte
2
O Xhalent também não funcionou para mim no início, então eu percebi que o parse deve ser passado um contêiner para os novos elementos, NÃO os próprios elementos - a solução rápida seria passar a forma inteira em vez do (s) elemento (s) - então funcionou como um encanto.
Per Hornshøj-Schierbeck
1
alguma ideia por que diz que validatornão está definido? Eu tenho a validação e a validação.unobtrusive referenciadas. A validação funciona até que eu chame esses códigos
Shawn Mclean
4
Obrigado. Funciona para conteúdo adicionado à página em visualizações parciais ASP.MVC adicionadas por meio de chamada AJAX.
Maksym Kozlenko
Obrigado, isso ajudou. Mas estou enfrentando um problema em que estou usando acordeões na página e se o acordeão estiver dobrado, ele não dispara as validações. Como posso consertar isso? Eu sei que se tivesse $('#form').validate({ignore: ""})
optado
1
Funcionou para mim em PHP. apenas adicionando essas informações porque todos os comentários apontam para .NET MVC. : P
finnTheHumin
42

Na verdade, gosto muito da simplicidade da solução @viggity e @Robins, então a transformei em um pequeno plugin rápido:

(function ($) {

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

        $.validator.unobtrusive.parse(form);

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

Exemplo de uso:

$("#DischargeOutcomeNumberOfVisits")
    .attr("data-val-range-min", this.checked ? "1" : "2")
    .updateValidation();
lambinador
fonte
substituir $ .validator.unobtrusive.parse ("#" + form.attr ("id")); com $ .validator.unobtrusive.parse (formulário);
Softlion
você também pode remover com segurança .first () já que o mais próximo retorna apenas 1 elemento.
Softlion
Legal, eu gosto dessa abordagem simples
nixon
Funciona perfeitamente para adicionar e remover atributos de val de dados
Julian Dormon
33

Estou tendo o mesmo problema. Descobri que não é possível chamar $ .validator.unobtrusive.parse () no mesmo formulário duas vezes. Ao carregar o formulário inicialmente do servidor, o formulário é analisado automaticamente pela biblioteca não obstrutiva. Quando você adiciona um elemento de entrada dinamicamente ao formulário e chama $ .validator.unobtrusive.parse () novamente, não funcionará. O mesmo vale para parseElement ().

O lib discreto chama o método validate do plugin jquery validate para definir todas as regras e mensagens. O problema é que, quando chamado novamente, o plugin não atualiza o novo conjunto de regras fornecido.

Encontrei uma solução grosseira: antes de chamar o método de análise na biblioteca não obstrutiva, jogo fora o validador de formulário:

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

Agora, quando o método validate é chamado pela biblioteca discreta, todas as regras e mensagens são recriadas, incluindo as entradas adicionadas dinamicamente.

Espero que isto ajude

Robin van der Knaap
fonte
Merece um +1 - é uma solução rudimentar como você mesmo disse, mas está bem claro o que ela faz e em que estado você fica depois
Per Hornshøj-Schierbeck
2
Do blog de Brad Wilsons: "Você também pode chamar a função jQuery.validator.unobtrusive.parseElement () para analisar um único elemento HTML." Qualquer ajuda?
jamesfm de
@Per Hornshøj-Schierbeck: @Robin van der Knaap: Passei as últimas 2-3 horas para descobrir isso !! Enfim, isso funcionou MARAVILHOSAMENTE. Bruto me faz sentir que não deveria usá-lo - por que eu não iria querer fazer isso?
KTF
2
A solução é um pouco grosseira porque todos os validadores são removidos e reintegrados, incluindo o validador adicionado dinamicamente. Seria melhor se apenas o validador adicionado dinamicamente fosse atualizado. @Xhalent oferece uma solução mais limpa em sua resposta, mas basicamente a biblioteca discreta do MS deve ser atualizada para este cenário.
Robin van der Knaap
9

Estou usando MVC 4 e JQuery 1.8, parece que o código a seguir é necessário para permitir que o Jquery valide o conteúdo injetado dinamicamente via Ajax ou Jquery no DOM.

Fiz uma função modular que aceita o objeto Jquery do elemento recém-adicionado. Se você clonou uma nova tabela com id tblContactsusando Jquery ao clicar em um botão, inclua a função abaixo em seu arquivo 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.
}

e chamá-lo assim:

fnValidateDynamicContent("#tblContacts")
Sundara Prabu
fonte
currform.validate (); deve ser currForm.validate (); (erro de digitação). Obrigado por compartilhar, no entanto, isso funcionou para mim
John Mc
1
@JohnMc corrigiu o erro de digitação agora
Sundara Prabu
Este é o único que funcionou para mim (mvc 4 e jquery 1.10)
The Muffin Man
4

Por que não usar a função de regras diretamente do documento de validação do jquery. Como isso:

$('#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');
zhangfx
fonte
2

Pegando a solução de Xhalent marcada como resposta acima, eu a expandi um pouco.

$.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];
            }
        }
    }
}

Basicamente, ele ainda funciona da mesma forma que a solução de Xhalent acima, mas adicionei a capacidade de remover regras para os elementos removidos do dom. Então, quando você remover elementos do Dom e quiser que essas regras de validação também sejam removidas, chame:

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

Encontrei o script de código de @Xhalent no meu código e ia excluí-lo porque não o estava usando, o que me levou a esta questão do SO.

Este código é bastante limpo e simples:

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();
}

Então, para chamar esta extensão jQuery, basta usar um seletor para pegar seu formulário:

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

Viola!

Brian Ogden
fonte
1

Tentei a resposta de Viggity e a princípio tudo pareceu funcionar. Mas depois de um tempo, percebi que a validação fica dolorosamente lenta quanto mais itens eu adicionei dinamicamente. O motivo é que sua solução não desvincula os manipuladores de eventos, mas adiciona novos a cada vez. Portanto, se você adicionar 5 itens, a validação será executada 6 vezes em vez de apenas uma vez. Para corrigir isso, você deve desvincular os eventos adicionalmente às chamadas removeData.

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

No caso de conteúdos dinâmicos, você precisa atualizar as validações discretas conforme abaixo e verificar se Formsão válidas durante o envio.

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');
    }
});
Jagdeesh Motegowda
fonte
0

Tenho mexido nisso por um tempo, descartando soluções e tentando novamente mais tarde (quando eu tivesse algum tempo livre, acredite ou não).

Não tenho certeza se esse comportamento teria mudado nas versões mais recentes do jquery (estamos usando 1.7.2) desde que este tópico foi criado ou comentado pela última vez, mas descobri que .parseElement(inputElement)funciona bem quando tento adicionar elementos criados dinamicamente a um formulário que já possui um validador carregado. Isso já foi sugerido por @jamesfm (15 de fevereiro '11) em um dos comentários acima, mas eu esqueci nas primeiras vezes que estava trabalhando nisso. Portanto, estou adicionando como uma resposta separada para torná-la mais óbvia e porque acho que é uma boa solução e não requer tanta sobrecarga. Pode não ser relevante para todas as questões levantadas nas respostas subsequentes, mas acho que seria uma solução para a questão original. Veja como fiz o meu funcionar:

//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);
Ben
fonte
0

Em primeiro lugar, acho que a chamada deve ser para .validator, não validar então você precisa passar o id do formulário

$.validator.unobtrusive.parse("#id");
Chris Allmark
fonte
Presumo que você tenha carregado os scripts jquery.validate & jquery.validate.unobtrusive e chamado os métodos Html.EnableClientValidation () e Html.EnableUnobtrusiveJavaScript (). Você pode postar um exemplo de código para sua página base e o formulário carregado dinamicamente, talvez?
Chris Allmark
0

Se você precisar adicionar e remover coisas com a possibilidade de que alguns erros já sejam exibidos, é o que eu vim. É baseado principalmente em diferentes ideias nesta página de perguntas. Eu uso o embutido em destroy()vez de apenas remover o atributo de dados para validar JQuery.

function resetValidator($form: JQuery) {
    $form.validate().destroy();

    //reset unobtrusive validation summary, if it exists
    $form.find("[data-valmsg-summary=true]")
        .removeClass("validation-summary-errors")
        .addClass("validation-summary-valid")
        .find("ul").empty();

    //reset unobtrusive field level, if it exists
    $form.find("[data-valmsg-replace]")
        .removeClass("field-validation-error")
        .addClass("field-validation-valid")
        .empty();

    $form.removeData("unobtrusiveValidation");
}

Você o chama antes de modificar dinamicamente as entradas no formulário. Em seguida, você reinicializa a validação depois.

resetValidator($form);
//add or remove inputs here ...
$.validator.unobtrusive.parse($form);

Nota: Se algumas ou todas as entradas foram validadas e contêm erros, esses erros serão zerados ... mas eles voltarão corretamente na próxima validação.

Yepeekai
fonte