Angularjs: input [text] ngChange dispara enquanto o valor está mudando


92

ngChange está disparando enquanto o valor está mudando (ngChange não são semelhantes ao evento onChange clássico). Como posso vincular o evento onChange clássico com angularjs, que só será acionado quando o conteúdo for confirmado?

Vinculação atual:

<input type="text" ng-model="name" ng-change="update()" />

Apenas procurando por algo semelhante e me ocorreu que você também pode querer considerar a atualização apenas quando o campo for válido. Dessa forma, o usuário pode ter dificuldade em obter a entrada em um formato válido (ou você pode ajudá-lo com solicitações e filtros), então você pode recompensá-lo com uma atualização quando acertar!
Steve Black

Uma solução muito mais flexível que permite especificar o evento a ser usado (não apenas desfocar) e outras propriedades devem ser incorporadas ao angular muito em breve: github.com/angular/angular.js/pull/2129
wired_in

Respostas:


96

Esta postagem mostra um exemplo de diretiva que atrasa as alterações do modelo em uma entrada até que o evento de desfoque seja acionado.

Aqui está um violino que mostra o ng-change funcionando com a nova diretiva ng-model-on-blur. Observe que este é um ligeiro ajuste ao violino original .

Se você adicionar a diretiva ao seu código, você alteraria sua ligação para:

<input type="text" ng-model="name" ng-model-onblur ng-change="update()" />

Aqui está a diretiva:

// override the default input to update on blur
angular.module('app', []).directive('ngModelOnblur', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        priority: 1, // needed for angular 1.2.x
        link: function(scope, elm, attr, ngModelCtrl) {
            if (attr.type === 'radio' || attr.type === 'checkbox') return;

            elm.unbind('input').unbind('keydown').unbind('change');
            elm.bind('blur', function() {
                scope.$apply(function() {
                    ngModelCtrl.$setViewValue(elm.val());
                });         
            });
        }
    };
});

Observação: como @wjin menciona nos comentários abaixo, esse recurso é compatível diretamente com o Angular 1.3 (atualmente em beta) via ngModelOptions. Veja os documentos para mais informações.


1
Cuidado, elm.unbind ('input') lança um 'TypeError' no Internet Explorer 8 para que as outras instruções unbind não sejam executadas. Eu resolvi isso movendo elm.unbind ('input') para uma linha separada e cercando-o com um bloco try / catch.
Rob Juurlink

1
Angular 1.2 tem uma ng-blurdiretriz: docs.angularjs.org/api/ng.directive:ngBlur
JJ Zabkar

5
Fiz esse código funcionar, mas tive que definir a prioridade da diretiva (ou seja, prioridade: 100). Estou usando o Angular 1.2.10. Meu palpite é que a diretiva ngModelOnblur estava em execução antes da diretiva ngModel, então não havia nada para desvincular ainda. Além disso, esta página faz parecer que pode haver uma solução integrada para isso no futuro: github.com/angular/angular.js/pull/1783
Alan West

4
NOTA Pelo menos no angular 1.3 esta funcionalidade é suportada na implementação padrão via ng-model-options="{ updateOn: 'default blur' }"Consulte a documentação
wjin

2
@RobJuurlink (ou qualquer outra pessoa que encontrar o IE8 'TypeError' ao tentar vincular ou desvincular a 'entrada') - Olhando a fonte do Angular, você verá que eles usam $snifferpara determinar se o navegador suporta 'entrada' e, se não, eles voltam para 'keydown'. Se você atualizar a diretiva acima para somente desvincular 'input' se $sniffer.hasEvent('input')retornar true - então você pode evitar esse erro e ainda trabalhar no IE8
bvaughn

32

Trata-se de adições recentes ao AngularJS, para servir como resposta futura (também para outra pergunta ).

Versões mais recentes do Angular (agora em 1.3 beta), o AngularJS oferece suporte nativo a essa opção, usando ngModelOptions, como

ng-model-options="{ updateOn: 'default blur', debounce: { default: 500, blur: 0 } }"

NgModelOptions docs

Exemplo:

<input type="text" name="username"
       ng-model="user.name"
       ng-model-options="{updateOn: 'default blur', debounce: {default: 500, blur: 0} }" />

Não funciona corretamente no 1.3 beta 5, mas foi corrigido na versão master atual 2602
manikanta

E é por isso que ainda está em beta e não pode ser usado em aplicativos do "mundo real", como o que estou construindo agora.
reaper_unique

Aqui está um ng-model-options como o módulo para Angular 1.2.x github.com/fergaldoyle/modelOptions
Fergal

8

Caso alguém esteja procurando suporte adicional para "enter" no teclado, aqui está uma atualização do violino fornecido por Gloppy

Código para vinculação de teclas:

elm.bind("keydown keypress", function(event) {
    if (event.which === 13) {
        scope.$apply(function() {
            ngModelCtrl.$setViewValue(elm.val());
        });
    }
});

5

Para qualquer pessoa que esteja lutando com o IE8 (ele não gosta de desvincular ('input'), encontrei uma maneira de contornar isso.

Injete $ sniffer em sua diretiva e use:

if($sniffer.hasEvent('input')){
    elm.unbind('input');
}

O IE8 se acalma se você fizer isso :)


ou apenas use try / catch
wired_in

4

De acordo com meu conhecimento, devemos usar ng-change com a opção select e no caso de caixa de texto devemos usar ng-blur.


Esta é a resposta correta agora, naquela época, quando a pergunta foi feita, aparentemente não existia.
downhand

Muito obrigado ... esta deve ser a resposta
Renjith


0

Substitua a entrada padrão no comportamento de mudança (chame a função apenas quando o foco de perda de controle e o valor forem alterados)

NOTA: ngChange não é semelhante ao evento onChange clássico, ele dispara o evento enquanto o valor está mudando. Esta diretiva armazena o valor do elemento quando ele obtém o foco
On blurs verifica se o novo valor foi alterado e, se for o caso, dispara o evento

@param {String} - nome da função a ser invocada quando o "onChange" deve ser acionado

@example <input my-on-change = "myFunc" ng-model = "model">

angular.module('app', []).directive('myOnChange', function () { 
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: {
            myOnChange: '='
        },
        link: function (scope, elm, attr) {
            if (attr.type === 'radio' || attr.type === 'checkbox') {
                return;
            }
            // store value when get focus
            elm.bind('focus', function () {
                scope.value = elm.val();

            });

            // execute the event when loose focus and value was change
            elm.bind('blur', function () {
                var currentValue = elm.val();
                if (scope.value !== currentValue) {
                    if (scope.myOnChange) {
                        scope.myOnChange();
                    }
                }
            });
        }
    };
});

0

Eu tive exatamente o mesmo problema e isso funcionou para mim. Adicione ng-model-updatee ng-keyuppronto! Aqui está a documentação

 <input type="text" name="userName"
         ng-model="user.name"
         ng-change="update()"
         ng-model-options="{ updateOn: 'blur' }"
         ng-keyup="cancel($event)" />
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.