Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ng-change delay, angularjs

I'm developing an application with angularjs, which shows some textfields in the screen with numeric data. They look quite like this:

<input type="text" ng-model="value" ng-change="controller.functions.valueChanged(value)">

The problem is everytime I write a number or I delete a number from the textfield, ng-change directive calls to the function. Is it possible to apply some king of delay to ng-change function?

like image 213
m.dorian Avatar asked Nov 24 '14 07:11

m.dorian


4 Answers

You can use ngModelOptions

debounce: integer value which contains the debounce model update value in milliseconds. A value of 0 triggers an immediate update.

Code

 <input type="text" ng-model-options="{ debounce: 1000 }" ng-model="value" ng-change="controller.functions.valueChanged(value)">
like image 57
Satpal Avatar answered Oct 17 '22 15:10

Satpal


Updated

you can use $timeout service to create delay function. this can be applied to other directive callback

angular.module('myApp', []);
angular.module('myApp')
  .controller('myCtrl', ["$scope", "$log", "$timeout",
    function($scope, $log, $timeout) {

      $scope.delay = (function() {
        var promise = null;
        return function(callback, ms) {
          $timeout.cancel(promise); //clearTimeout(timer);
          promise = $timeout(callback, ms); //timer = setTimeout(callback, ms);
        };
      })();

      $scope.doSomeThing = function(value) {
        var current = new Date();
        $scope.result = 'value:' + $scope.foo + ', last updated:' + current;
      };

    }
  ]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
  <h3>$timeout delay demo</h3>
  <div>
    <input ng-model="foo" ng-change="delay(doSomeThing, 1000)" type="text" />
  </div>
  <div>Result: {{result}}</div>
</div>
like image 36
aifarfa Avatar answered Oct 17 '22 14:10

aifarfa


I am using AngularJs 1.2.x and stumble upon the ng-change issue of firing on each change. ng-blur can be used but it fires even though there is no change in the value. So both cannot be used efficiently.

With Angularjs 1.3.x, things are easier using ng-model-options like below

to invoke change function "onBlur"

ng-change="ctrl.onchange()" ng-model-options="{updateOn: 'blur'}"

And

to delay invocation of change function by 500ms

ng-change="ctrl.onchange()" ng-model-options='{ debounce: 500 }'"

Now coming to back to the question of getting such things with AngularJs 1.2.x

to invoke change function "onBlur"

html

<input type="text" ng-model="ctrl.a.c" sd-change-on-blur="ctrl.onchange()" /> or

<input type="text" ng-model="ctrl.a.c" sd-change-on-blur="ctrl.onchange(ctrl.a.c)" />

JS

app.directive('sdChangeOnBlur', function() {
  return {
    restrict: 'A',
    scope: {
      sdChangeOnBlur: '&'
    },
    link: function(scope, elm, attrs) {
      if (attrs.type === 'radio' || attrs.type === 'checkbox')
        return;

      var parameters = getParameters(attrs.sdChangeOnBlur);

      var oldValue = null;
      elm.bind('focus', function() {
        scope.$apply(function() {
          oldValue = elm.val();
        });
      })
    
      elm.bind('blur', function() {
        scope.$apply(function() {
          if (elm.val() != oldValue) {
            var params = {};
            if (parameters && parameters.length > 0) {
              for (var n = 0; n < parameters.length; n++) {
                params[parameters[n]] = scope.$parent.$eval(parameters[n]);
              }
            } else {
              params = null;
            }

            if (params == null) {
              scope.sdChangeOnBlur();
            } else {
              scope.sdChangeOnBlur(params)
            }
          }
        });
      });
    }
  };
});

function getParameters(functionStr) {
  var paramStr = functionStr.slice(functionStr.indexOf('(') + 1, functionStr.indexOf(')'));
  var params;
  if (paramStr) {
    params = paramStr.split(",");
  }
  var paramsT = [];
  for (var n = 0; params && n < params.length; n++) {
    paramsT.push(params[n].trim());
  }
  return paramsT;
}

to delay invocation of change function by 500ms

html

<input type="text" ng-model="name" sd-change="onChange(name)" sd-change-delay="300"/>

OR

<input type="text" ng-model="name" sd-change="onChange()" sd-change-delay="300"/>

JS

app.directive('sdChange', ['$timeout',
  function($timeout) {
    return {
      restrict: 'A',
      scope: {
        sdChange: '&',
        sdChangeDelay: '@' //optional
      },
      link: function(scope, elm, attr) {
        if (attr.type === 'radio' || attr.type === 'checkbox') {
          return;
        }

        if (!scope.sdChangeDelay) {
          scope.sdChangeDelay = 500; //defauld delay
        }

        var parameters = getParameters(attr.sdChange);

        var delayTimer;
        elm.bind('keydown keypress', function() {
          if (delayTimer !== null) {
            $timeout.cancel(delayTimer);
          }

          delayTimer = $timeout(function() {
            var params = {};
            if (parameters && parameters.length > 0) {
              for (var n = 0; n < parameters.length; n++) {
                params[parameters[n]] = scope.$parent.$eval(parameters[n]);
              }
            } else {
              params = null;
            }

            if (params == null) {
              scope.sdChange();
            } else {
              scope.sdChange(params)
            }
            delayTimer = null;
          }, scope.sdChangeDelay);

          scope.$on(
            "$destroy",
            function(event) {
              $timeout.cancel(delayTimer);
              console.log("Destroyed");
            }
          );
        });
      }
    };
  }
]);

function getParameters(functionStr) {
  var paramStr = functionStr.slice(functionStr.indexOf('(') + 1, functionStr.indexOf(')'));
  var params;
  if (paramStr) {
    params = paramStr.split(",");
  }
  var paramsT = [];
  for (var n = 0; params && n < params.length; n++) {
    paramsT.push(params[n].trim());
  }
  return paramsT;
}

plnkrs for both approaches are

http://plnkr.co/edit/r5t0KwMtNeOhgnaidKhS?p=preview

http://plnkr.co/edit/9PGbYGCDCtB52G8bJkjx?p=info

like image 27
Yogesh Manware Avatar answered Oct 17 '22 15:10

Yogesh Manware


The easiest way to do it is to set a timeout inside the controller.functions.valueChanged function.

Angularjs has ngModelOptions directive which is very useful for this kind of things. You can try to set

ng-model-options="{ debounce: 1000 }"

for the timeout before the model changes. You can also use

ng-model-options="{ updateOn: 'blur' }"

To update the model only when focus leaves the element.

like image 2
Ijon Tichy Avatar answered Oct 17 '22 14:10

Ijon Tichy