Angularjs evita el envío de formularios cuando falla la validación de entrada

155

Estoy escribiendo un formulario de inicio de sesión simple usando angularjs con alguna validación de entrada del lado del cliente para verificar que el nombre de usuario y la contraseña no estén vacíos y tengan más de tres caracteres. Ver el siguiente código:

<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>

Y el controlador:

var controller = function($scope) {

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

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

};

El problema es que login.submitse llamará a la función incluso si la entrada no es válida. ¿Es posible prevenir este comportamiento?

Como nota al margen, puedo mencionar que también uso bootstrap y requirejs.

Runar Halse
fuente
¿podemos usarlo sin etiqueta de formulario y aún form.valid funcionaría en ng-click!
Rizwan Patel

Respuestas:

329

Tu puedes hacer:

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

No hay necesidad de verificaciones del controlador.

cyberwombat
fuente
8
Esta debería ser la respuesta aceptada. No es necesario verificar nada en el controlador
Howie
10
No estoy de acuerdo con "no hay necesidad de verificaciones del controlador". ¿Qué sucede si no se realizan comprobaciones de validación adicionales en la función de envío?
SlaterCodes
66
Realice la validación adicional en la directiva. Actualice la validación con $ setValidity.
TMB
@ b1tsh1ft Sería preferible que siempre se conozca el estado actual de la validación en lugar de averiguarlo en el envío. Ese es un poco el objetivo de la validación angular. No puede tener ninguna indicación visual de un estado no válido cuando el alcance no lo sabe.
Kevin Beal
1
He intentado este enfoque, pero parece que evalúa ambos (es decir, no cortocircuita el valor booleano después de &&haber intentado pasar el valor como parte del envío y confirmado que el valor en sí mismo es "falso"
Archimedes Trajano
67

Cambie el botón de enviar a:

<button type="submit" ng-disabled="loginform.$invalid">Login</button>
Bijan
fuente
1
Gran respuesta. ¡Tú Molas!
Harsha.Vaswani
24
¡Aún puede enviar el formulario si presiona Intro mientras un campo de entrada tiene el foco!
fabsenet
2
@Red: Ver comentario arriba.
Andrei Rînea
Si bien la desactivación del botón suele ser la mejor manera de resolver esto, no es lo que solicitó el OP.
downhand
1
La combinación de esta técnica para proporcionar comentarios visuales para el usuario junto con la respuesta de @Yashua para evitar el envío del formulario es perfecta. ¡Gracias!
tkarls
43

Entonces, la respuesta sugerida de TheHippo no funcionó para mí, en cambio terminé enviando el formulario como un parámetro a la función de esta manera:

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

Esto hace que el formulario esté disponible en el método del controlador:

$scope.login = {
    submit : function(form) {
        if(form.$valid)....
    }
Runar Halse
fuente
Tuve el mismo problema. Sin embargo, usar su método funcionó.
Panda4Man
Esto es un poco mejor que la Respuesta aceptada porque la verificación de formulario. $ Valid se realiza en JS y no en HTML.
cellepo
Sin embargo, no creo que necesite pasar loginForm para enviar la función: creo que $ scope.loginform debería estar disponible en el JS (al menos para mí), por lo que podría reemplazar el formulario. $ Válido con $ scope.loginform . $ válido.
cellepo
13

Sus formularios se colocan automáticamente en $ scope como un objeto. Se puede acceder a través de$scope[formName]

A continuación se muestra un ejemplo que funcionará con su configuración original y sin tener que pasar el formulario como parámetro en ng-submit.

var controller = function($scope) {

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

        }
    }

};

Ejemplo de trabajo: http://plnkr.co/edit/BEWnrP?p=preview

davidmdem
fuente
44
¿Cómo accedería al formulario si utiliza la notación "Como controlador" en lugar de $ scope?
masimplo
@masimakopoulos: Simplemente puede usar if(this.loginform.$invalid)... en su lugar, pero luego, en el HTML, debe darle el nombre al formulario <form name="ctrl.loginform" ... Y eso es asumiendo que está usando <div ng-controller="controller as ctrl"..en su sintaxis de controlador. Gracias @JoshCaroll
Bart
13

HTML:

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

En tu controlador:

$scope.login = {
    onSubmit: function(event) {
        if (dataIsntValid) {
            displayErrors();
            event.preventDefault();
        }
        else {
            submitData();
        }
    }
}
TheHippo
fuente
¿Es realmente necesario hacer la verificación dentro del método de envío? Esto significará que debe verificar que la entrada cumple con todos los criterios tanto en el marcado (a través de angular) como en el código js. En mi opinión, angular no debería llamar a onsubmit cuando la entrada no es válida. ver benlesh.com/2012/11/angular-js-form-validation.html diciendo "no se puede llamar ng-submit hasta que todo el formulario sea $ válido"
Runar Halse
También puede deshabilitar el botón para no permitir el envío si el formulario es válido. También puede usar el formulario ng-submit. Si angular no maneja la validación del formulario, entonces event.preventDefault()es el camino a seguir, detener el envío del formulario.
TheHippo
@RunarHalse Es realmente necesario hacer la verificación. Estoy de acuerdo con usted en que no debería ser así. La razón por la que no se llama al método onsubmit en el ejemplo que vinculó es porque ese ejemplo no tiene un conjunto de no validación. El navegador está evitando que los eventos se generen, no Angular. Aquí está su ejemplo con nobalidate en: plnkr.co/edit/R0C8XA?p=preview . Observe que se envía el formulario.
davidmdem
perfecto justo lo que estaba buscando, una forma de bloquear el envío de formularios usando angular
setebos
3

Aunque no es una solución directa para la pregunta de los OP, si su formulario está dentro de un ng-appcontexto, pero desea que Angular lo ignore por completo, puede hacerlo explícitamente utilizando la ngNonBindabledirectiva:

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

Solo para agregar a las respuestas anteriores,

Estaba teniendo 2 botones regulares como se muestra a continuación. (Sin tipo = "enviar" en cualquier 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>

No importa cuánto lo intente, presionando enter una vez que el formulario fue válido , se llamó al botón "Borrar formulario", borrando todo el formulario.

Como solución alternativa,

Tuve que agregar un botón de envío ficticio que estaba deshabilitado y oculto. Y este botón ficticio tenía que estar encima de todos los demás botones como se muestra a continuación .

<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>

Bueno, mi intención nunca fue presentar en Enter, por lo que el truco dado anteriormente funciona bien.

vighnu
fuente
puede detener el envío al presionar enter también eliminando el atributo on ng-submit de su formulario
Kieran
1

Sé que este es un hilo viejo pero pensé que también haría una contribución. Mi solución es similar a la publicación ya marcada como respuesta. Algunas comprobaciones de JavaScript en línea hacen el truco.

ng-click="form.$invalid ? alert('Please correct the form') : saveTask(task)"
Dave Russell
fuente
1
¡Aún puede enviar el formulario si presiona Intro mientras un campo de entrada tiene el foco!
Yevgeniy Afanasyev
0

Sé que es tarde y me respondieron, pero me gustaría compartir las cosas interesantes que hice. Creé una directiva ng-validate que engancha el envío del formulario, luego emite prevent-default si $ eval es 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();            
    });
  };
});

En tu html:

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