Angularjs: input [text] ngChange se déclenche pendant que la valeur change

92

ngChange se déclenche pendant que la valeur change (ngChange n'est pas similaire à l'événement onChange classique). Comment puis-je lier l'événement onChange classique avec angularjs, qui ne se déclenchera que lorsque le contenu sera validé?

Reliure actuelle:

<input type="text" ng-model="name" ng-change="update()" />
Kaffee Bohne
la source
Je cherchais juste quelque chose de similaire et il m'est apparu que vous pourriez également envisager de mettre à jour uniquement lorsque le champ est valide. De cette façon, l'utilisateur peut avoir du mal à obtenir l'entrée dans un format valide (ou vous pouvez l'aider avec des invites et des filtres), vous pouvez alors les récompenser avec une mise à jour quand ils le font correctement!
Steve Black
Une solution beaucoup plus flexible qui permet de spécifier l'événement à utiliser (pas seulement le flou) et d'autres propriétés devrait être intégrée à angular très bientôt: github.com/angular/angular.js/pull/2129
wired_in

Réponses:

96

Cet article montre un exemple de directive qui retarde les changements de modèle dans une entrée jusqu'à ce que l' événement de flou se déclenche.

Voici un violon qui montre le ng-change fonctionnant avec la nouvelle directive ng-model-on-blur. Notez qu'il s'agit d'une légère modification du violon d'origine .

Si vous ajoutez la directive à votre code, vous modifieriez votre liaison comme suit:

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

Voici la directive:

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

Remarque: comme @wjin le mentionne dans les commentaires ci-dessous, cette fonctionnalité est prise en charge directement dans Angular 1.3 (actuellement en version bêta) via ngModelOptions. Consultez la documentation pour plus d'informations.

Gloopy
la source
1
Attention, elm.unbind ('input') renvoie une 'TypeError' dans Internet Explorer 8 afin que les autres instructions unbind ne soient pas exécutées du tout. Je l'ai résolu en déplaçant elm.unbind ('input') sur une ligne séparée et en l'entourant d'un bloc try / catch.
Rob Juurlink
1
Angular 1.2 a une ng-blurdirective: docs.angularjs.org/api/ng.directive:ngBlur
JJ Zabkar
5
J'ai fait fonctionner ce code, mais j'ai dû définir la priorité de la directive (c'est-à-dire la priorité: 100). J'utilise Angular 1.2.10. Je suppose que la directive ngModelOnblur était en cours d'exécution avant la directive ngModel donc il n'y avait encore rien à dissocier. En outre, cette page donne l'impression qu'il pourrait y avoir une solution intégrée pour cela à l'avenir: github.com/angular/angular.js/pull/1783
Alan West
4
REMARQUE Au moins dans angular 1.3, cette fonctionnalité est prise en charge dans l'implémentation par défaut via ng-model-options="{ updateOn: 'default blur' }"Voir la documentation
wjin
2
@RobJuurlink (ou toute autre personne qui rencontre l'IE8 'TypeError' en essayant de se lier ou de se dissocier à 'input') - En regardant la source d'Angular, vous verrez qu'ils l'utilisent $snifferpour déterminer si le navigateur prend en charge 'input' et sinon, ils retombent en «keydown». Si vous mettez à jour la directive ci-dessus pour ne dissocier 'input' que si $sniffer.hasEvent('input')retourne true - vous pouvez éviter cette erreur et continuer à travailler dans IE8
bvaughn
32

Il s'agit des ajouts récents à AngularJS, pour servir de réponse future (également pour une autre question ).

Angular nouvelles versions (maintenant en version 1.3 beta), AngularJS prend en charge nativement cette option, en utilisant ngModelOptions, comme

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

Documentation sur NgModelOptions

Exemple:

<input type="text" name="username"
       ng-model="user.name"
       ng-model-options="{updateOn: 'default blur', debounce: {default: 500, blur: 0} }" />
Manikanta
la source
Cela ne fonctionne pas correctement dans la version 1.3 beta 5, mais corrigé dans la build principale actuelle 2602
Manikanta
Et c'est pourquoi il est toujours en version bêta et n'est pas utilisable dans des applications "réelles" comme celle que je construis actuellement.
reaper_unique
Voici un module ng-model-options comme un module pour Angular 1.2.x github.com/fergaldoyle/modelOptions
Fergal
8

Au cas où quelqu'un d'autre chercherait une prise en charge supplémentaire des touches "Entrée", voici une mise à jour du violon fourni par Gloppy

Code pour la liaison de touches:

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

Pour tous ceux qui luttent avec IE8 (il n'aime pas le détachement ('input'), j'ai trouvé un moyen de contourner cela.

Injectez $ sniffer dans votre directive et utilisez:

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

IE8 se calme si vous faites cela :)

Brad Barrow
la source
ou utilisez simplement try / catch
wired_in
4

Selon ma connaissance, nous devrions utiliser ng-change avec l'option de sélection et dans le cas de la zone de texte, nous devrions utiliser ng-blur.

Anni
la source
C'est la bonne réponse maintenant, à l'époque, quand la question était posée, elle n'existait apparemment pas.
downhand
Merci beaucoup ... cela devrait être la réponse
Renjith
3

N'est-ce pas utiliser $ scope. $ Watch pour refléter mieux les changements de la variable scope?

Zahiduzzaman
la source
1
Matière à réflexion: benlesh.com/2013/10/title.html (intitulé: vous ne devriez probablement pas utiliser $ watch dans vos contrôleurs)
BenB
0

Remplacer le comportement onChange de l'entrée par défaut (appeler la fonction uniquement lorsque le focus de perte de contrôle et la valeur ont été modifiés)

REMARQUE: ngChange ne ressemble pas à l'événement onChange classique , il déclenchement de l'événement alors que la valeur change Cette directive stocke la valeur de l'élément quand il obtient le focus
sur brouille il vérifie si la nouvelle valeur a changé et s'il déclenche l'événement

@param {String} - nom de la fonction à invoquer lorsque "onChange" doit être déclenché

@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
la source
0

J'ai eu exactement le même problème et cela a fonctionné pour moi. Ajoutez ng-model-updateet ng-keyupet vous êtes prêt à partir! Voici la documentation

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