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()" />
Kaffee Bohne
fonte
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.

Gloopy
fonte
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} }" />
manikanta
fonte
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());
        });
    }
});
Bing Han
fonte
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 :)

Brad Barrow
fonte
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.

Anni
fonte
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
3

Não está usando $ scope. $ Watch para refletir melhor as alterações da variável de escopo?

Zahiduzzaman
fonte
1
Alimento para reflexão: benlesh.com/2013/10/title.html (intitulado: você provavelmente não deveria usar $ watch em seus controladores)
BenB
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();
                    }
                }
            });
        }
    };
});
user3119770
fonte
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)" />
iMario999
fonte