92

ngChange is firing while the value is changing (ngChange are not similiar to the classic onChange event). How can i bind the classic onChange event with angularjs, that will only fire when the contents are commited?

Current binding:

<input type="text" ng-model="name" ng-change="update()" />
Kaffee Bohne
  • 1,457
  • 3
  • 14
  • 17
  • Just looking for something similar and it occured to me that you might also want to consider updating only when the field is valid. That way the user can struggle with getting the input into a valid format (or you might help them with prompts and filters) then you can reward them with an update when they get it right! – Steve Black Mar 18 '13 at 22:46
  • A much more flexible solution that allows for specifying the event to use (not just blur) and other properties should be built in to angular very soon: https://github.com/angular/angular.js/pull/2129 – wired_in Feb 24 '14 at 20:24

8 Answers8

99

This post shows an example of a directive that delays the model changes to an input until the blur event fires.

Here is a fiddle that shows the ng-change working with the new ng-model-on-blur directive. Note this is a slight tweak to the original fiddle.

If you add the directive to your code you would change your binding to this:

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

Here is the 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());
                });         
            });
        }
    };
});

Note: as @wjin mentions in the comments below this feature is supported directly in Angular 1.3 (currently in beta) via ngModelOptions. See the docs for more info.

Gloopy
  • 37,767
  • 15
  • 103
  • 71
  • Thanks, that works. I changed "blur" with "change" and it works also. – Kaffee Bohne Aug 08 '12 at 18:42
  • 1
    Caution, elm.unbind('input') throws an 'TypeError' in Internet Explorer 8 so the other unbind statements won't be executed at all. I solved it by moving elm.unbind('input') to a separate line and surrounded it with a try/catch block. – Rob Juurlink Aug 26 '13 at 10:17
  • 1
    Angular 1.2 has an `ng-blur` directive: http://docs.angularjs.org/api/ng.directive:ngBlur – JJ Zabkar Sep 26 '13 at 21:53
  • @JJ ng-blur does something else, unless you want to explain how he'd use that here. – Frank Schwieterman Jan 08 '14 at 02:19
  • 5
    I made this code work, but I had to set the priority of the directive (i.e., priority: 100). I'm using Angular 1.2.10. My guess is that the ngModelOnblur directive was running prior to the ngModel directive so there was nothing to unbind yet. Also, this page makes it seem like there may be a built in solution for this in the future: https://github.com/angular/angular.js/pull/1783 – Alan West Jan 28 '14 at 22:29
  • @Alan West: that pull request was closed in favor of this one: https://github.com/angular/angular.js/pull/2129 which hopefully should be merged in soon – wired_in Feb 24 '14 at 19:57
  • @wired_in it has been merged in v1.3.0. Let's wait till it's out of beta! – emp May 20 '14 at 08:54
  • @emp Well it's about time! :) – wired_in May 20 '14 at 14:57
  • @SamanthaAtkins updated the example to set the priority for this directive to work with Angular 1.2.x like Alan mentioned above. – Gloopy Jun 13 '14 at 21:40
  • Thanks. I moved to the semi-official solution in 1.3 betas. How would one find a list of what priorities are set on a version of angularjs for what events/handlers? –  Jun 13 '14 at 22:20
  • Some other priorities changed in 1.2 (see [this](https://code.angularjs.org/1.2.17/docs/guide/migration#directive-priority) for more info) but I think the issue here more related to [this](http://stackoverflow.com/questions/20261424/render-stopped-working-at-angular-1-2-2-file-validation-directive). – Gloopy Jun 13 '14 at 22:36
  • 4
    NOTE At least in angular 1.3 this functionality is supported in the default implementation via `ng-model-options="{ updateOn: 'default blur' }"` See [documentation](https://docs.angularjs.org/api/ng/directive/ngModelOptions) – wjin Jun 17 '14 at 23:12
  • 2
    @RobJuurlink (or anyone else who encounters the IE8 'TypeError' when trying to bind or unbind to 'input') - Looking at Angular's source, you'll see that they use `$sniffer` to determine if the browser supports 'input' and if not, they fall back to 'keydown'. If you update the above directive to only unbind 'input' if `$sniffer.hasEvent('input')` returns true - then you can avoid that error and still work in IE8 – bvaughn Aug 27 '14 at 04:15
  • Here's an ng-model-options like module for Angular 1.2.x http://github.com/fergaldoyle/modelOptions – Fergal Sep 27 '14 at 18:40
  • Thanks for mentioning ngModelOptions - I didn't know about that directive! – jstol Sep 30 '14 at 19:04
33

This is about recent additions to AngularJS, to serve as future answer (also for another question).

Angular newer versions (now in 1.3 beta), AngularJS natively supports this option, using ngModelOptions, like

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

NgModelOptions docs

Example:

<input type="text" name="username"
       ng-model="user.name"
       ng-model-options="{updateOn: 'default blur', debounce: {default: 500, blur: 0} }" />
Community
  • 1
  • 1
manikanta
  • 8,100
  • 5
  • 59
  • 66
8

In case anyone else looking for additional "enter" keypress support, here's an update to the fiddle provided by Gloppy

Code for keypress binding:

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

For anyone struggling with IE8 (it doesn't like the unbind('input'), I found a way around it.

Inject $sniffer into your directive and use:

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

IE8 calms down if you do this :)

Brad Barrow
  • 1,036
  • 10
  • 18
4

According to my knowledge we should use ng-change with the select option and in textbox case we should use ng-blur.

Anni
  • 35
  • 2
3

Isn't using $scope.$watch to reflect the changes of scope variable better?

Zahiduzzaman
  • 197
  • 1
  • 3
  • 1
    Food for thought: http://www.benlesh.com/2013/10/title.html (titled: you probably shouldn't use $watch in your controllers) – BenB Oct 05 '14 at 13:55
0

Override the default input onChange behavior (call the function only when control loss focus and value was change)

NOTE: ngChange is not similar to the classic onChange event it firing the event while the value is changing This directive stores the value of the element when it gets the focus
On blurs it checks whether the new value has changed and if so it fires the event

@param {String} - function name to be invoke when the "onChange" should be fired

@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

I had exactly the same problem and this worked for me. Add ng-model-update and ng-keyup and you're good to go! Here is the docs

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