Angularjs: input[text] ngChange fires while the value is changing

The question:

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()" />

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

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.

Method 2

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} }" />

Method 3

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

Method 4

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 🙂

Method 5

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

Method 6

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

Method 7

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

Method 8

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)" />


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment