Angularjs: input [text] ngChange se activa mientras el valor cambia

92

ngChange se activa mientras el valor cambia (ngChange no es similar al evento clásico onChange). ¿Cómo puedo vincular el evento onChange clásico con angularjs, que solo se activará cuando se confirmen los contenidos?

Enlace actual:

<input type="text" ng-model="name" ng-change="update()" />
Kaffee Bohne
fuente
Solo buscaba algo similar y se me ocurrió que también podría considerar actualizar solo cuando el campo sea válido. De esa manera, el usuario puede tener dificultades para obtener la entrada en un formato válido (o puede ayudarlo con indicaciones y filtros) y luego puede recompensarlo con una actualización cuando lo haga bien.
Steve Black
Una solución mucho más flexible que permite especificar el evento a usar (no solo desenfoque) y otras propiedades deben integrarse en angular muy pronto: github.com/angular/angular.js/pull/2129
wired_in

Respuestas:

96

Esta publicación muestra un ejemplo de una directiva que retrasa los cambios de modelo en una entrada hasta que se activa el evento de desenfoque .

Aquí hay un violín que muestra el ng-change trabajando con la nueva directiva ng-model-on-blur. Tenga en cuenta que esto es un ligero ajuste al violín original .

Si agrega la directiva a su código, cambiaría su enlace a esto:

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

Aquí está la directiva:

// 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());
                });         
            });
        }
    };
});

Nota: como @wjin menciona en los comentarios a continuación, esta función es compatible directamente en Angular 1.3 (actualmente en beta) a través de ngModelOptions. Consulte los documentos para obtener más información.

Gloopy
fuente
1
Precaución, elm.unbind ('input') arroja un 'TypeError' en Internet Explorer 8 para que las otras declaraciones de desvinculación no se ejecuten en absoluto. Lo resolví moviendo elm.unbind ('input') a una línea separada y lo rodeé con un bloque try / catch.
Rob Juurlink
1
Angular 1.2 tiene una ng-blurdirectiva: docs.angularjs.org/api/ng.directive:ngBlur
JJ Zabkar
5
Hice que este código funcionara, pero tuve que establecer la prioridad de la directiva (es decir, prioridad: 100). Estoy usando Angular 1.2.10. Supongo que la directiva ngModelOnblur se estaba ejecutando antes que la directiva ngModel, por lo que todavía no había nada que desvincular. Además, esta página hace que parezca que puede haber una solución integrada para esto en el futuro: github.com/angular/angular.js/pull/1783
Alan West
4
NOTA Al menos en angular 1.3 esta funcionalidad es compatible con la implementación predeterminada a través de ng-model-options="{ updateOn: 'default blur' }"Ver documentación
wjin
2
@RobJuurlink (o cualquier otra persona que se encuentre con el 'TypeError' de IE8 al intentar enlazar o desvincular a 'input') - Al mirar la fuente de Angular, verá que usan $snifferpara determinar si el navegador admite 'input' y, de no ser así, vuelven a "keydown". Si actualiza la directiva anterior para desvincular solo 'input' si $sniffer.hasEvent('input')devuelve verdadero, entonces puede evitar ese error y aún trabajar en IE8
bvaughn
32

Se trata de adiciones recientes a AngularJS, para servir como respuesta futura (también para otra pregunta ).

Angular versiones más nuevas (ahora en 1.3 beta), AngularJS admite de forma nativa esta opción, usando ngModelOptions, como

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

Documentos de NgModelOptions

Ejemplo:

<input type="text" name="username"
       ng-model="user.name"
       ng-model-options="{updateOn: 'default blur', debounce: {default: 500, blur: 0} }" />
manikanta
fuente
No funciona correctamente en 1.3 beta 5, pero se corrigió en la compilación maestra actual 2602
manikanta
Y es por eso que todavía está en versión beta y no se puede usar en aplicaciones del "mundo real" como la que estoy construyendo ahora.
reaper_unique
Aquí hay un módulo ng-model-options como para Angular 1.2.x github.com/fergaldoyle/modelOptions
Fergal
8

En caso de que alguien más busque soporte adicional de pulsación de teclas "enter", aquí hay una actualización del violín proporcionada por Gloppy

Código para el enlace de pulsaciones de teclas:

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

Para cualquiera que esté luchando con IE8 (no le gusta la desvinculación ('entrada'), encontré una forma de evitarlo.

Inyecte $ sniffer en su directiva y use:

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

IE8 se calma si haces esto :)

Brad Barrow
fuente
o simplemente use try / catch
wired_in
4

Según mi conocimiento, deberíamos usar ng-change con la opción de selección y en el caso del cuadro de texto deberíamos usar ng-blur.

Anni
fuente
Esta es la respuesta correcta ahora, en ese entonces, cuando se hizo la pregunta, aparentemente no existía.
downhand
Muchas gracias ... esta debería ser la respuesta
Renjith
3

¿No está usando $ scope. $ Watch para reflejar mejor los cambios de la variable de alcance?

Zahiduzzaman
fuente
1
Alimento para el pensamiento: benlesh.com/2013/10/title.html (titulado: probablemente no deberías usar $ watch en tus controladores)
BenB
0

Anule la entrada predeterminada en el comportamiento de cambio (llame a la función solo cuando se modifique el foco y el valor de la pérdida de control)

NOTA: ngChange no es similar al evento onChange clásico, dispara el evento mientras cambia el valor.Esta directiva almacena el valor del elemento cuando se enfoca
en desenfoques, comprueba si el nuevo valor ha cambiado y, de ser así, dispara el evento.

@param {String}: nombre de la función que se invocará cuando se active "onChange"

@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();
                    }
                }
            });
        }
    };
});
usuario3119770
fuente
0

Tuve exactamente el mismo problema y esto funcionó para mí. ¡Agrega ng-model-updatey ng-keyuplisto! Aquí están los documentos

 <input type="text" name="userName"
         ng-model="user.name"
         ng-change="update()"
         ng-model-options="{ updateOn: 'blur' }"
         ng-keyup="cancel($event)" />
iMario999
fuente