Envie o formulário pressionando Enter com AngularJS

364

Nesse caso específico, quais opções eu tenho para fazer com que essas entradas chamem uma função quando pressiono 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');
    };
}])
todos
fonte

Respostas:

518

Angular suporta isso fora da caixa. Você já tentou o ngSubmit no seu elemento de formulário?

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

EDIT: pelo comentário sobre o botão enviar, consulte Enviando um formulário pressionando enter sem um botão enviar, que fornece a solução de:

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

Se você não gostar da solução oculta do botão de envio, precisará vincular uma função do controlador ao evento Enter ou pressionar a tecla Enter. Isso normalmente requer uma diretiva personalizada, mas a biblioteca AngularUI já possui uma boa solução de pressionamento de tecla. Veja http://angular-ui.github.com/

Após adicionar a lib angularUI, seu código seria algo como:

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

ou você pode vincular a tecla Enter a cada campo individual.

Além disso, consulte estas perguntas de SO para criar uma diretiva keypres simples: Como posso detectar onKeyUp no AngularJS?

EDIT (28/08/2014): No momento em que essa resposta foi escrita, ng-keypress / ng-keyup / ng-keydown não existia como diretivas nativas no AngularJS. Nos comentários abaixo, @ darlan-alves tem uma solução muito boa com:

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

eterps
fonte
9
Só funciona se eu tiver um botão de envio dentro do formulário também.
ali
3
Observe também que este TEM um <form> que parece não funcionar em <someElement ng-form = "" ...> que é o que eu costumo usar.
John Culviner
11
Basta deixar uma observação: se você chegou aqui e os botões de envio não funcionam, você pode estar usando um nome incorreto para a função de envio. Eu estava usando tolamente ng-submit="join()"um controlador de registro que tinha $scope.join = function() {...}e alterava o nome dessa função para que foo()o envio do botão enter funcionasse novamente.
testador
11
Na época esta resposta foi escrito, NG-keypress / ng-keyup não existia no Angular
eterps
69
ng-keyup="$event.keyCode == 13 && myFunc()"Realmente :) incrível
Gonchar Denys
286

Se você deseja chamar a função sem formulário, pode usar minha diretiva 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>

Submeto outras diretrizes impressionantes no meu twitter e na minha conta principal .

EpokK
fonte
2
Existe uma maneira inteligente de fazer com que isso seja acionado a qualquer momento que eles pressionem enter enquanto estiverem na sua página?
Derek Adair
11
@ EpokK Quero desativar a tecla Enter para todo o formulário, como posso fazer isso? (Eu quero evitar formulário de inscrição com enter)
Antonio Max
47
muito bom, mas de acordo com a recomendação do AngularJs, você não deve criar diretivas, serviços ou filtros com o prefixo ng-, caso um lançamento oficial mais tarde use o mesmo nome.
Neil S
5
Ótima solução. Acabei de alterar o nome para keyBind e esta linha "if (event.which === 13) {" para this "if (event.which === Number (attrs.key)) {" E então minha entrada para "< input type = "text" bind-key = "doSomething ()" key = "13"> "para que eu pudesse reutilizá-lo para diferentes eventos-chave.
18768 Brian F
4
IMPORTANTE: Adicionar eventos keydowne keypresssem vírgula para delimitá-los significa que ambos podem ser acionados simultaneamente. É provável que ocorram erros $ rootScope: inprog. A adição de uma vírgula entre eles cria um disjuntivo e garante que apenas ocorra o ciclo $ digest. Não foi possível aplicar a edição, pois é apenas um caractere.
21780 Ryan Murphy
197

Se você tiver apenas uma entrada, poderá usar a tag do formulário.

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

Se você possui mais de uma entrada ou não deseja usar a tag de formulário ou deseja anexar a funcionalidade da tecla enter a um campo específico, é possível incorporá-la a uma entrada específica da seguinte maneira:

<input ng-keyup="$event.keyCode == 13 && myFunc()" ...>
MILÍMETROS
fonte
2
Esta é uma maneira inteligente de fazer isso mesmo sem ter que escrever o seu próprio directiva
Charlie Martin
59
Ainda mais curto: <input ng-keyup = "$ event.keyCode == 13 && myFunc ()" ... />
Darlan Alves
Funciona muito bem, usei a diretiva ng-keyup, mas tenho um grande problema: se eu tiver apenas um campo de texto, ele envia o formulário completo (postagem), mas não quero isso. Eu já tentei ng-keyup = "$ event.keyCode == 13 && onTextBoxKeyUp ($ event)" e na função "event.preventDefault ();", mas não ajudou; (
SharpNoiZy
isso é mais curto, mas mantendo a abordagem DRY em minha mente, eu ainda criaria uma diretiva e a usaria com a diretiva.
Atul Chaudhary
Isso pode ser problemático, dependendo do contexto - eu tenho um formulário no qual tenho dois botões (interface semelhante ao assistente), com os botões 'voltar' e 'próximo', o comportamento padrão ao clicar na tecla 'enter' é o botão voltar. Agora, ao usar o código acima, o botão voltar é CLICADO PRIMEIRO e, em seguida, o código myFunction () é chamado (o que, por sua vez, passa para a próxima parte do assistente). A solução do @ EpokK funciona perfeita para mim.
Vishwajeet Vatharkar
33

Eu queria algo um pouco mais extensível / semântico do que as respostas fornecidas, então escrevi uma diretiva que pega um objeto javascript de maneira semelhante à incorporada ngClass:

HTML

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

Os valores do objeto são avaliados no contexto do escopo da diretiva - verifique se estão entre aspas simples, caso contrário todas as funções serão executadas quando a diretiva for carregada (!)

Então, por exemplo: em esc : 'clear()'vez 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
fonte
Eu recebo um erro de análise de HTML do angluarjs quando tento adicionar uma variável de string na função. key-bind = "{enter: 'vm.doTheThing (' myVar ')'}"
MaylorTaylor
Também notei que, se eu usar a função com um Objeto como variável, a função ocorrerá mais de uma vez. O código abaixo remove mais de 2 itens quando eu executo isso. key-bind = "{enter: 'vm.removeItem (item)'}"
MaylorTaylor
11
@MaylorTaylor para a primeira emissão, usar diferentes tipos de citações ou seja'vm.doTheThing("myVar")'
AlexFoxGill
14

Diretiva muito boa, limpa e simples com 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
fonte
5

Se você deseja validação de dados também

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

A adição importante aqui é $loginForm.$validque validará o formulário antes de executar a função. Você precisará adicionar outros atributos para validação que estão além do escopo desta pergunta.

Boa sorte.

Akash
fonte
2

Só queria ressaltar que, no caso de haver um botão de envio oculto, você pode simplesmente usar a diretiva ngShow e configurá-la como false da seguinte forma:

HTML

<form ng-submit="myFunc()">
    <input type="text" name="username">
    <input type="submit" value="submit" ng-show="false">
</form>
Landesko
fonte
11
+1 <button ng-show="false"></button>é ainda mais curto. Não há necessidade de definir o valor de um botão invisível.
Musa
Entendi que, para acionar a diretiva ng-submit, uma entrada de type="submit"... Suponho que o valor possa ser ignorado, mas acredito que a primeira seja necessária.
landesko
1

Use ng-submit e apenas agrupe as duas entradas em tags de formulário 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>

O agrupamento de cada campo de entrada em sua própria tag de formulário permite que ENTER chame o envio em qualquer formulário. Se você usar uma tag de formulário para ambos, precisará incluir um botão de envio.

Myclamm
fonte
0

Será um pouco mais limpo usando uma classe CSS em vez de repetir estilos embutidos.

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

FWIW - Aqui está uma diretiva que eu usei para um modal básico de confirmação / alerta de inicialização, sem a necessidade de um <form>

(basta mudar a ação de clique do jQuery para o que quiser e adicionar data-easy-dismissà sua tag 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
fonte