Angularjs impede o envio de formulários quando a validação de entrada falha

155

Estou escrevendo um formulário de login simples usando angularjs com alguma validação de entrada do lado do cliente para verificar se o nome de usuário e a senha não estão vazios e têm mais de três caracteres. Veja o código abaixo:

<form name="loginform" novalidate ng-submit="login.submit()" class="css-form">
    <fieldset>

        <div class="control-group input-prepend">
            <span class="add-on"><i class="icon-user"></i></span>
            <input type="text" ng-model="login.username" name="username" required ng-minlength="3" placeholder="username" />
        </div>

        <div class="control-group input-prepend">
            <span class="add-on"><i class="icon-lock"></i></span>
            <input type="password" ng-model="login.password" name="password" required ng-minlength="3" placeholder="" />
        </div>

        <div class="control-group">
            <input class="btn" type="submit" value="Log in">
        </div>

    </fieldset>
</form>

E o controlador:

var controller = function($scope) {

    $scope.login = {
        submit: function() {

            Console.info($scope.login.username + ' ' + $scope.login.password);
        }
    }

};

O problema é que a login.submitfunção será chamada mesmo que a entrada não seja válida. É possível evitar esse comportamento?

Como observação, posso mencionar que eu também uso o bootstrap e o requirejs.

Runar Halse
fonte
podemos usá-lo sem tag de formulário e ainda form.valid funcionaria com ng-click!
Rizwan Patel

Respostas:

329

Você pode fazer:

<form name="loginform" novalidate ng-submit="loginform.$valid && login.submit()">

Não há necessidade de verificações do controlador.

cyberwombat
fonte
8
Essa deve ser a resposta aceita. Não há necessidade de verificação de qualquer coisa no controlador
Howie
10
Não concordo com "não há necessidade de verificações de controladores". E se realizarmos verificações adicionais de validação na função de envio.
SlaterCodes
6
Execute a validação adicional na diretiva. Atualize a validação com $ setValidity.
TMB
@ b1tsh1ft Seria preferível que o estado atual da validação fosse sempre conhecido, em vez de descobrir no envio. Esse é o ponto principal da validação do angular. Você não pode ter nenhuma indicação visual de um estado inválido quando o escopo não o conhece.
Kevin Beal
1
Eu tentei essa abordagem, mas parece que ela avalia ambas (ou seja, não provoca um curto-circuito no booleano depois que &&eu tentei passar o valor como parte do envio e confirmei que o valor em si é "falso"
Archimedes Trajano
67

Altere o botão enviar para:

<button type="submit" ng-disabled="loginform.$invalid">Login</button>
Bijan
fonte
1
Ótima resposta. Você é demais!
Harsha.Vaswani
24
Você ainda pode enviar o formulário se pressionar Enter enquanto um campo de entrada estiver focado!
fabsenet
2
@ Red: Veja o comentário acima.
Andrei Rînea 17/11/2015
Embora desabilitar o botão seja geralmente a melhor maneira de resolver isso, não é o que o OP solicitou.
downhand
1
Combinar essa técnica para fornecer feedback visual ao usuário, juntamente com a resposta @Yashua, para impedir o envio de formulários, é perfeito. Obrigado!
Tkarls # 12/17
43

Portanto, a resposta sugerida pelo TheHippo não funcionou para mim; acabei enviando o formulário como parâmetro para a função da seguinte maneira:

<form name="loginform" novalidate ng-submit="login.submit(loginForm)" class="css-form">

Isso disponibiliza o formulário no método do controlador:

$scope.login = {
    submit : function(form) {
        if(form.$valid)....
    }
Runar Halse
fonte
Eu tive o mesmo problema. Usando seu método funcionou, no entanto.
Panda4Man
Isso é um pouco melhor que a Resposta Aceita porque o formulário. $ Valid check é feito em JS e não em HTML.
Cellepo # 26/17
No entanto, não acho que você precise passar o loginForm para enviar a função: Acho que $ scope.loginform deve estar disponível no JS (pelo menos era para mim), para que você possa substituir o formulário. $ Valid por $ scope.loginform . $ válido.
Cellepo # 26/17
13

Seus formulários são automaticamente colocados em $ scope como um objeto. Pode ser acessado via$scope[formName]

Abaixo está um exemplo que funcionará com sua configuração original e sem a necessidade de passar o próprio formulário como parâmetro no ng-submit.

var controller = function($scope) {

    $scope.login = {
        submit: function() {
            if($scope.loginform.$invalid) return false;

        }
    }

};

Exemplo de trabalho: http://plnkr.co/edit/BEWnrP?p=preview

davidmdem
fonte
4
Como você acessaria o formulário se usasse a notação "As Controller" em vez de $ scope?
masimplo
@masimakopoulos: Você pode simplesmente usar if(this.loginform.$invalid).. em vez disso, mas no HTML você deve dar o nome ao formulário <form name="ctrl.loginform" ... E isso assumindo que você esteja usando <div ng-controller="controller as ctrl"..em seu controlador como sintaxe. Obrigado @JoshCaroll
Bart
13

HTML:

<div class="control-group">
    <input class="btn" type="submit" value="Log in" ng-click="login.onSubmit($event)">
</div>

No seu controlador:

$scope.login = {
    onSubmit: function(event) {
        if (dataIsntValid) {
            displayErrors();
            event.preventDefault();
        }
        else {
            submitData();
        }
    }
}
TheHippo
fonte
é realmente necessário fazer a verificação dentro do método onsubmit? Isso significa que você deve verificar se a entrada atende a todos os critérios, tanto na marcação (via angular) quanto no código js. Na minha opinião angular não deve chamar o onsubmit quando a entrada é inválida. veja benlesh.com/2012/11/angular-js-form-validation.html dizendo "ng a apresentar não pode ser chamado até que o formulário inteiro é de R $ válido"
Runar Halse
Você também pode desativar o botão para não permitir o envio se o formulário for válido. Você também pode usar o formulário um ng-submit. Se o angular não lida com a validação de formulário, event.preventDefault()é o caminho a seguir, para interromper o envio do formulário.
thehippo
@RunarHalse É realmente necessário fazer a verificação. Concordo com você que não deve ser assim. O motivo pelo qual o método onsubmit não é chamado no exemplo que você vinculou é porque esse exemplo não possui um conjunto de validação. O navegador está impedindo que os eventos aumentem, não Angular. Aqui está seu exemplo com nobalidate em: plnkr.co/edit/R0C8XA?p=preview . Observe que o formulário é enviado.
Davidmdem 13/08
aperfeiçoar apenas o que eu estava procurando, uma forma de forma de bloco submeter usando angulares
Setebos
3

Embora não seja uma solução direta para a questão dos OPs, se seu formulário estiver dentro de um ng-appcontexto, mas você desejar que o Angular o ignore completamente, faça isso explicitamente usando a ngNonBindablediretiva:

<form ng-non-bindable>
  ...
</form>
Ian Clark
fonte
2

Apenas para adicionar às respostas acima,

Eu estava usando dois botões regulares, como mostrado abaixo. (Nenhum tipo = "enviar" em qualquer lugar)

<button ng-click="clearAll();" class="btn btn-default">Clear Form</button>
<button ng-disabled="form.$invalid" ng-click="submit();"class="btn btn-primary pull-right">Submit</button>

Não importa o quanto tentei, pressionando enter uma vez que o formulário era válido , o botão "Limpar formulário" foi chamado, limpando todo o formulário.

Como solução alternativa,

Eu tive que adicionar um botão de envio fictício que estava desativado e oculto. E esse botão fictício tinha que estar em cima de todos os outros botões, como mostrado abaixo .

<button type="submit" ng-hide="true" ng-disabled="true">Dummy</button>

<button ng-click="clearAll();" class="btn btn-default">Clear Form</button>

<button ng-disabled="form.$invalid" ng-click="submit();"class="btn btn-primary pull-right">Submit</button>

Bem, minha intenção nunca foi enviar no Enter, então o hack fornecido acima funciona bem.

vighnu
fonte
você pode parar de enviar on pressione enter também removendo o em NG-enviar atributo de sua forma
Kieran
1

Sei que esse é um tópico antigo, mas pensei em fazer uma contribuição. Minha solução sendo semelhante à postagem já marcada como resposta. Algumas verificações de JavaScript embutidas são suficientes.

ng-click="form.$invalid ? alert('Please correct the form') : saveTask(task)"
Dave Russell
fonte
1
Você ainda pode enviar o formulário se pressionar enter enquanto um campo de entrada estiver focado!
Yevgeniy Afanasyev
0

Sei que é tarde e fui atendida, mas gostaria de compartilhar as coisas legais que fiz. Criei uma diretiva ng-validate que conecta o envio do formulário e, em seguida, emite prevent-default se o $ eval for falso:

app.directive('ngValidate', function() {
  return function(scope, element, attrs) {
    if (!element.is('form'))
        throw new Error("ng-validate must be set on a form elment!");

    element.bind("submit", function(event) {
        if (!scope.$eval(attrs.ngValidate, {'$event': event}))
            event.preventDefault();
        if (!scope.$$phase)
            scope.$digest();            
    });
  };
});

No seu html:

<form name="offering" method="post" action="offer" ng-validate="<boolean expression">
Ran Cohen
fonte