web-dev-qa-db-ja.com

lodashを使用して、AngularJSで監視されている<input>をデバウンスまたはスロットルできますか?

$ scope.idにバインドされている<input>フィールドを監視する次のものがあります。入力フィールドの値が変わるたびに、監視機能が実行されます。

$scope.$watch("id", function (id) {

   // code that does something based on $scope.id

});

ユーザーが値を変更している間、キーを押すたびにコードが実行されないように、これにタイムアウトを設定したり、_lodashでこれをデバウンスする方法はありますか。

ユーザーが1秒間入力を停止した後、時計内のコードブロックが実行されるように、1秒間の遅延が必要です。入力値はいつでも変更される可能性があることに注意してください。たとえば、値が「1」または「10」または「1000」の場合に呼び出される関数が必要です。これは、候補を含む検索ボックスがGoogleで機能する方法に似ています。ユーザーが999と入力した場合、関数を呼び出す必要があります。彼が9を削除して99になったら、関数を呼び出す必要があります。

_lodashを利用できるので、それを使用するソリューションが私のニーズに最適かもしれません。

52
user1943020

それはあなたが探しているものですか?

$scope.$watch("id", _.debounce(function (id) {
    // Code that does something based on $scope.id
    // This code will be invoked after 1 second from the last time 'id' has changed.
}, 1000));

ただし、その関数内で$ scopeを変更したい場合は、_.debounce関数が$timeoutを内部で使用しない限り、$scope.$apply(...)でラップする必要があります(私が理解していない限り)Angularは、$scopeで行った変更を認識しません。

[〜#〜] update [〜#〜]

更新された質問について-はい、コールバック関数本体全体をラップする必要があります

$scope.$apply()

$scope.$watch("id", _.debounce(function (id) {
    // This code will be invoked after 1 second from the last time 'id' has changed.
    $scope.$apply(function(){
        // Code that does something based on $scope.id
    })
}, 1000));
66
Alex Vayda

NgModelOptionsをAngular 1.3.0で使用できます

HTML:

<div ng-controller="Ctrl">
  <form name="userForm">
    Name:
    <input type="text" name="userName"
           ng-model="user.name"
           ng-model-options="{ debounce: 1000 }" />
    <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
  </form>
  <pre>user.name = <span ng-bind="user.name"></span></pre>
</div>

詳細: https://docs.angularjs.org/api/ng/directive/ngModelOptions

80
AhmedRiyad

私は質問がlodashの解決策を求めていることを知っています。とにかくangularのみのソリューション:

app.factory('debounce', function($timeout) {
    return function(callback, interval) {
        var timeout = null;
        return function() {
            $timeout.cancel(timeout);
            var args = arguments;
            timeout = $timeout(function () { 
                callback.apply(this, args); 
            }, interval);
        };
    }; 
}); 

コントローラー内:

app.controller('BlaCtrl', function(debounce) {

    $scope.$watch("id", debounce(function (id) {
        ....
    }, 1000));

});
33
jdachtera

これをディレクティブにカプセル化できます。ソース: https://Gist.github.com/tommaitland/7579618

<input type="text" ng-model="id" ng-debounce="1000">

Javascript

app.directive('ngDebounce', function ($timeout) {
  return {
      restrict: 'A',
      require: 'ngModel',
      priority: 99,
      link: function (scope, Elm, attr, ngModelCtrl) {
          if (attr.type === 'radio' || attr.type === 'checkbox') {
              return;
          }

          var delay = parseInt(attr.ngDebounce, 10);
          if (isNaN(delay)) {
              delay = 1000;
          }

          Elm.unbind('input');

          var debounce;
          Elm.bind('input', function () {
              $timeout.cancel(debounce);
              debounce = $timeout(function () {
                  scope.$apply(function () {
                      ngModelCtrl.$setViewValue(Elm.val());
                  });
              }, delay);
          });
          Elm.bind('blur', function () {
              scope.$apply(function () {
                  ngModelCtrl.$setViewValue(Elm.val());
              });
          });
      }
  };
});
6
jessegavin