Envíe el formulario al presionar Enter con AngularJS

364

En este caso particular, ¿qué opciones tengo para hacer que estas entradas llamen una función cuando presiono Enter?

// HTML view //
<form>
    <input type="text" ng-model="name" <!-- Press ENTER and call myFunc --> />
    <br />
    <input type="text" ng-model="email" <!-- Press ENTER and call myFunc --> />
</form>

// Controller //
.controller('mycontroller', ['$scope',function($scope) {
    $scope.name = '';
    $scope.email = '';
    // Function to be called when pressing ENTER
    $scope.myFunc = function() {
       alert('Submitted');
    };
}])
Ali
fuente

Respuestas:

518

Angular es compatible con esto fuera de la caja. ¿Has probado ngSubmit en tu elemento de formulario?

<form ng-submit="myFunc()" ng-controller="mycontroller">
   <input type="text" ng-model="name" />
    <br />
    <input type="text" ng-model="email" />
</form>

EDITAR: según el comentario sobre el botón de envío, vea Enviar un formulario presionando enter sin un botón de envío que da la solución de:

<input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;"/>

Si no le gusta la solución de botón de envío oculto, deberá vincular una función de controlador al evento de pulsación de tecla Enter o keyup. Esto normalmente requiere una directiva personalizada, pero la biblioteca AngularUI ya tiene una buena solución de pulsación de teclas configurada. Ver http://angular-ui.github.com/

Después de agregar la biblioteca angularUI, su código sería algo así como:

<form ui-keypress="{13:'myFunc($event)'}">
  ... input fields ...
</form>

o puede vincular la pulsación de tecla enter a cada campo individual.

Además, vea estas preguntas SO para crear una directiva simple de keypres: ¿Cómo puedo detectar onKeyUp en AngularJS?

EDITAR (2014-08-28): en el momento en que se escribió esta respuesta, ng-keypress / ng-keyup / ng-keydown no existía como directivas nativas en AngularJS. En los comentarios a continuación, @ darlan-alves tiene una solución bastante buena con:

<input ng-keyup="$event.keyCode == 13 && myFunc()"... />

Eterps
fuente
99
Solo funciona si también tengo un botón de envío dentro del formulario.
Ali
3
También tenga en cuenta que esto tiene que ser un <form> no parece funcionar en <someElement ng-form = "" ...> que es lo que tiendo a usar.
John Culviner
1
Solo dejo una nota: si ha terminado aquí y los botones de envío no funcionan para usted, podría estar usando un mal nombre para su función de envío. Estuve tontamente usando ng-submit="join()"un controlador de registro que tenía $scope.join = function() {...}y cambiando el nombre de esa función para que el foo()envío del botón enter funcionara nuevamente.
probador
1
En el momento en que se escribió esta respuesta, ng-keypress / ng-keyup no existía en Angular
eterps
69
ng-keyup="$event.keyCode == 13 && myFunc()"Realmente increíble :)
Gonchar Denys
286

Si desea llamar a la función sin formulario, puede usar mi directiva ngEnter:

Javascript :

angular.module('yourModuleName').directive('ngEnter', function() {
        return function(scope, element, attrs) {
            element.bind("keydown keypress", function(event) {
                if(event.which === 13) {
                    scope.$apply(function(){
                        scope.$eval(attrs.ngEnter, {'event': event});
                    });

                    event.preventDefault();
                }
            });
        };
    });

HTML :

<div ng-app="" ng-controller="MainCtrl">
    <input type="text" ng-enter="doSomething()">    
</div>

Presento otras directivas impresionantes en mi cuenta de Twitter y Gist .

EpokK
fuente
2
¿Hay una manera inteligente de hacer que esto se active cada vez que presionan enter mientras están en su página?
Derek Adair el
1
@EpokK Quiero deshabilitar la tecla Intro para todo el formulario, ¿cómo puedo hacer eso? (Quiero evitar el envío del formulario con enter)
Antonio Max
47
muy agradable, pero según la recomendación de AngularJs, no debe crear directivas, servicios o filtros con el prefijo ng-, en caso de que una versión oficial más tarde use el mismo nombre.
Neil S
55
Gran solución Acabo de cambiar el nombre a keyBind y esta línea "if (event.which === 13) {" a this "if (event.which === Number (attrs.key)) {" Y luego mi entrada a "< input type = "text" bind-key = "doSomething ()" key = "13"> "para poder reutilizarlo en diferentes eventos clave.
Brian F
44
IMPORTANTE: Agregar los eventos keydowny keypresssin una coma para delimitarlos significa que ambos pueden dispararse simultáneamente. Es probable que esto genere $ rootScope: errores inprog. Agregar una coma entre ellos crea un disyuntivo y garantiza que solo se produzca el ciclo $ digest. No se pudo aplicar la edición, ya que solo se trata de un único carácter.
Ryan Miller
197

Si solo tiene una entrada, puede usar la etiqueta de formulario.

<form ng-submit="myFunc()" ...>

Si tiene más de una entrada, o no desea usar la etiqueta del formulario, o si desea adjuntar la funcionalidad de la tecla Intro a un campo específico, puede incorporarlo a una entrada específica de la siguiente manera:

<input ng-keyup="$event.keyCode == 13 && myFunc()" ...>
MM
fuente
2
Esta es una manera inteligente de hacer esto sin siquiera tener que escribir su propia directiva
Charlie Martin
59
Aún más corto: <input ng-keyup = "$ event.keyCode == 13 && myFunc ()" ... />
Darlan Alves
Funciona muy bien, utilicé la directiva ng-keyup, pero tengo un gran problema con ella, si solo tengo un campo de texto, envía el formulario completo (devolución de datos) pero no quiero eso. Ya probé ng-keyup = "$ event.keyCode == 13 && onTextBoxKeyUp ($ event)" y en la función "event.preventDefault ();", pero no ayudó; (
SharpNoiZy
esto es más corto, pero teniendo en cuenta el enfoque DRY, todavía crearía una directiva y la usaría con la directiva.
Atul Chaudhary
Esto puede ser problemático dependiendo del contexto: tengo una forma en la que tengo dos botones (asistente como interfaz), con el botón 'Atrás' y 'Siguiente', el comportamiento predeterminado al hacer clic en la tecla 'Enter' es el botón Atrás. Ahora, cuando se usa el código anterior, el botón Atrás se CLICA PRIMERO, y luego se llama al código myFunction () (que a su vez, pasa a la siguiente parte del asistente) Entonces, mi asistente retrocede un tiempo y luego avanza. La solución de @ EpokK funciona perfecta para mí.
Vishwajeet Vatharkar
33

Quería algo un poco más extensible / semántico que las respuestas dadas, así que escribí una directiva que toma un objeto javascript de manera similar al incorporado ngClass:

HTML

<input key-bind="{ enter: 'go()', esc: 'clear()' }" type="text"></input>

Los valores del objeto se evalúan en el contexto del alcance de la directiva; asegúrese de que están encerrados entre comillas simples; de lo contrario, todas las funciones se ejecutarán cuando se cargue la directiva (!)

Así por ejemplo: esc : 'clear()' lugar deesc : clear()

Javascript

myModule
    .constant('keyCodes', {
        esc: 27,
        space: 32,
        enter: 13,
        tab: 9,
        backspace: 8,
        shift: 16,
        ctrl: 17,
        alt: 18,
        capslock: 20,
        numlock: 144
    })
    .directive('keyBind', ['keyCodes', function (keyCodes) {
        function map(obj) {
            var mapped = {};
            for (var key in obj) {
                var action = obj[key];
                if (keyCodes.hasOwnProperty(key)) {
                    mapped[keyCodes[key]] = action;
                }
            }
            return mapped;
        }

        return function (scope, element, attrs) {
            var bindings = map(scope.$eval(attrs.keyBind));
            element.bind("keydown keypress", function (event) {
                if (bindings.hasOwnProperty(event.which)) {
                    scope.$apply(function() {
                         scope.$eval(bindings[event.which]);
                    });
                }
            });
        };
    }]);
AlexFoxGill
fuente
Recibo un error de análisis HTML de angluarjs cuando intento agregar una variable de cadena en la función. key-bind = "{enter: 'vm.doTheThing (' myVar ')'}"
MaylorTaylor
También noté que si uso la función con un Objeto como variable, la función ocurre más de una vez. El siguiente código elimina 2+ elementos cuando ejecuto esto. key-bind = "{enter: 'vm.removeItem (item)'}"
MaylorTaylor
1
@MaylorTaylor para el primer número, use diferentes tipos de citas, es decir'vm.doTheThing("myVar")'
AlexFoxGill
14

Muy buena, limpia y simple directiva con shift + enter support:

app.directive('enterSubmit', function () {
    return {
        restrict: 'A',
        link: function (scope, elem, attrs) {
            elem.bind('keydown', function(event) {
                 var code = event.keyCode || event.which;
                 if (code === 13) {
                       if (!event.shiftKey) {
                            event.preventDefault();
                            scope.$apply(attrs.enterSubmit);
                       }
                 }
            });
        }
    }
});
Vlatko
fuente
5

Si quieres validación de datos también

<!-- form -->
<form name="loginForm">
...
  <input type="email" ng-keyup="$loginForm.$valid && $event.keyCode == 13 && signIn()" ng-model="email"... />
  <input type="password" ng-keyup="$loginForm.$valid && $event.keyCode == 13 && signIn()" ng-model="password"... />
</form>

La adición importante aquí es la $loginForm.$validque validará el formulario antes de ejecutar la función. Tendrá que agregar otros atributos para la validación que está más allá del alcance de esta pregunta.

Buena suerte.

Akash
fuente
2

Solo quería señalar que en el caso de tener un botón de envío oculto, puede usar la directiva ngShow y configurarla como falsa de esta manera:

HTML

<form ng-submit="myFunc()">
    <input type="text" name="username">
    <input type="submit" value="submit" ng-show="false">
</form>
Landesko
fuente
1
+1 <button ng-show="false"></button>es aún más corto. No es necesario establecer el valor de un botón invisible.
musa
Entendí que para activar la directiva ng-submit, una entrada de type="submit"... supongo que el valor podría ignorarse, pero creo que lo primero es necesario.
landesko
1

Use ng-submit y simplemente ajuste ambas entradas en etiquetas de formulario separadas:

<div ng-controller="mycontroller">

  <form ng-submit="myFunc()">
    <input type="text" ng-model="name" <!-- Press ENTER and call myFunc --> />
  </form>

  <br />

  <form ng-submit="myFunc()">
    <input type="text" ng-model="email" <!-- Press ENTER and call myFunc --> />
  </form>

</div>

Ajustar cada campo de entrada en su propia etiqueta de formulario permite que ENTER invoque el envío en cualquiera de los formularios. Si usa una etiqueta de formulario para ambos, deberá incluir un botón de envío.

Myclamm
fuente
0

Será un poco más ordenado usando una clase CSS en lugar de repetir estilos en línea.

CSS

input[type=submit] {
    position: absolute;
    left: -9999px;
}

HTML

<form ng-submit="myFunc()">
    <input type="text" ng-model="name" />
    <br />
    <input type="text" ng-model="email" />
    <input type="submit" />
</form>
guya
fuente
0

FWIW: aquí hay una directiva que he usado para un modo de arranque básico de confirmación / alerta, sin la necesidad de un <form>

(solo cambie la acción de clic de jQuery para lo que quiera y agréguelo data-easy-dismissa su etiqueta modal)

app.directive('easyDismiss', function() {
    return {
        restrict: 'A',
        link: function ($scope, $element) {

            var clickSubmit = function (e) {
                if (e.which == 13) {
                    $element.find('[type="submit"]').click();
                }
            };

            $element.on('show.bs.modal', function() {
                $(document).on('keypress', clickSubmit);
            });

            $element.on('hide.bs.modal', function() {
                $(document).off('keypress', clickSubmit);
            });
        }
    };
});
Andrew Appleby
fuente