【问题标题】:Can I debounce or throttle a watched <input> in AngularJS using _lodash?我可以在 AngularJS 中使用 _lodash 去抖动或限制观看的 <input> 吗?
【发布时间】:2014-02-01 01:18:37
【问题描述】:

我有以下对绑定到 $scope.id 的 &lt;input&gt; 字段进行监视。每次输入字段值更改时,都会执行 watch 函数:

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

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

});

有没有办法我可以对此设置超时或使用 _lodash 去抖动,以便代码 当用户更改值时,不会在每次按键时执行。

我想要的是 延迟一秒,以便在用户停止输入一秒后 手表内的代码块运行。请注意,输入值可能随时更改。例如,如果值为“1”或“10”或“1000”,我需要调用该函数。这类似于带有建议的搜索框在 Google 中的工作方式。如果用户输入 999,那么我需要调用该函数。如果他删除了一个 9,所以它是 99,那么我需要调用该函数。

我确实有 _lodash 可用,因此使用的解决方案可能最适合我的需求。

【问题讨论】:

    标签: javascript angularjs lodash


    【解决方案1】:

    您可以在 Angular 1.3.0 中使用 ngModelOptions

    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

    【讨论】:

    • 如果您能够使用 Angular 1.3,这绝对是您的最佳选择!
    【解决方案2】:

    这就是你要找的东西吗?

    $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,则应将其包装为 $scope.$apply(...),除非 _.debounce 函数在内部使用 $timeout(据我所知,它不会这样做)不会知道您在$scope 上所做的更改。

    更新

    关于更新的问题 - 是的,您需要用

    包装整个回调函数体

    $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));
    

    【讨论】:

    • Wajda - 是的,我认为这是需要的,但我不确定是否应该使用去抖动或油门?我试图寻找那里的例子,但没有很多。你怎么看,并且会在这里实现同样的目标?
    • debouncethrottle 的区别可以用电梯来比喻。如果有几个人进入电梯,除非有足够的时间关门,否则它不会运行——这就是“去抖动”。如果电梯很小并且有太多人想进入,它最终必须运行(比如最多 5 人) - 这就是“油门”
    • 我更新了问题以显示您的建议。我应该将所有内容都包装在 $scope.$apply() 中,还是可以在代码末尾和函数内运行 $scope.$apply()?
    • 因此,如果您想在用户输入时永远等待,请使用debounce'. If you want the actions to be performed after some maximum delay even if the user didn't stop typing - use throttle`
    • 我已经更新了我的答案 - 是的,最好用 $apply 包装代码。推荐的方法。最后调用 $apply() 只有在代码块没有抛出异常并且你的 $apply() 被实际调用时才会起作用。在这方面,使用 $apply() 包装代码是安全的 - Angualar 将知道您的代码中发生的任何事情。
    【解决方案3】:

    我知道这个问题需要一个 lodash 解决方案。无论如何,这是一个只有角度的解决方案:

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

    【讨论】:

    • 但是,不应该更像var args = arguments; timeout = $timeout(function () { callback.apply(this, args); }, interval); 吗? id 参数将在您的代码中未定义并完全丢失。
    • 是的,你是对的,谢谢。在简单的手表中,您可以只从作用域中获取值,但当然最好将参数传递给回调。
    【解决方案4】:

    您可以将其封装在指令中。来源: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());
                  });
              });
          }
      };
    });
    

    【讨论】:

      猜你喜欢
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 2018-10-01
      • 2021-10-24
      • 2015-06-28
      • 2014-02-02
      • 2023-04-08
      • 2021-04-30
      相关资源
      最近更新 更多