【问题标题】:Implement a delay on $scope.$watch在 $scope.$watch 上实现延迟
【发布时间】:2013-12-05 10:18:16
【问题描述】:

我想知道是否可以在 $scope.$watch 上实现一点延迟。我有以下查询服务器的内容,所以我想在查询服务器之前评估query 之前稍微延迟一下。我注意到,如果您快速输入,它会感到困惑并且不会发送正确的信息:

$scope.$watch("query", function () {
    $scope.loading = true;
    returnFactory.query($scope.query).then(function (returns) {
        $scope.returns = returns;
        $scope.loading = false;
    });
});

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    如果模型“查询”是 Html 标记或 Angular 指令,您可以使用 ng-model-option 用于 Ej:

    <input type ng-model="query" ng-model-options="{ updateOn: 'default blur', debounce: { 'default':
     2000, 'blur': 1 } }" />
    

    您可以在此处查看 Angular 文档:https://docs.angularjs.org/api/ng/directive/ngModelOptions

    【讨论】:

    • 上面的超时方法会导致一些奇怪的页面刷新。此代码按预期工作。我在数据表中有一个字段,只需添加 { debounce: 500 } 就可以让他们输入数字而无需在工作过程中刷新表格。谢谢!
    【解决方案2】:

    通常我会说使用 angular 的 $timeout 来解决这个延迟,但你还不能清除这个超时。

    //编辑:你可以。

    设置一个超时并清除它,如果这个观察者被触发得足够快。

    像这样:

    var timeoutCode;
    var delayInMs = 2000;
    $scope.$watch("query", function(query) {
     clearTimeout(timeoutCode);  //does nothing, if timeout alrdy done
     timeoutCode = setTimeout(function(){   //Set timeout
         $scope.loading = true;
         returnFactory.query(query).then(function(returns) {
           $scope.returns = returns;
           $scope.loading = false;
         });
     },delayInMs);
    });
    

    http://jsfiddle.net/4FuyY/

    更新感谢stevie 这可以通过 Angular 的 $timeout 来实现。

        var timeoutPromise;
        var delayInMs = 2000;
        $scope.$watch("query", function(query) {
         $timeout.cancel(timeoutPromise);  //does nothing, if timeout alrdy done
         timeoutPromise = $timeout(function(){   //Set timeout
             $scope.loading = true;
             returnFactory.query(query).then(function (returns) {
               $scope.returns = returns;
               $scope.loading = false;
             });
         },delayInMs);
        });
    

    【讨论】:

    • 编辑器上有一个按钮可以将代码标签出来。不需要&lt;code&gt; 标签。只需选择,按下按钮即可添加选项卡。成功了:)
    • $timeout 可以$timeout.cancel(timeoutPromise)清除。
    • @Phill:我现在知道了,谢谢!致 Stewie:好点子,我没有找到任何关于此的 api 文档?
    • 如果您想手动编写去抖动代码,这是一种方法。更好的是简单地使用 ng-model-options 中内置的 debounce,如下面的解决方案。
    【解决方案3】:

    我喜欢使用Lo-Dash,它提供了两个非常有用的功能:debouncethrottle,这正是您想要的。假设你想确保它每 150 毫秒只调用一次函数:

    function update() {
     $scope.loading = true;
        returnFactory.query($scope.query).then(function (returns) {
            $scope.returns = returns;
            $scope.loading = false;
        });
    }
    
    $scope.$watch("query", function () {
       _.throttle(update, 150);
    });
    

    throttle 函数可让您控制何时调用 update 函数(后沿或前沿)。

    我一直在我的应用程序中使用 Lo-Dash。它是我必备的库……比 jQuery 更有用。但是,如果您不想包含整个库,您可以创建一个仅包含 throttledebounce 函数的 Lo-Dash 自定义版本。

    【讨论】:

    • 嗯,很有趣。之前没听说过Lo-Dash!感谢您的链接!
    • 没问题。将 Lo-Dash 视为 Underscore 的一个分支。 Underscore 的所有功能(以及更多功能)并且性能更高。
    • Lodash 很棒,对于任何项目来说都是必须的,但值得注意的是,使用 lodash 中的debounce 会阻止单元测试。如果需要单元测试,可能需要考虑基于$timeout 的去抖动实现,因为 angular-mocks 提供了$timeout.flush()
    【解决方案4】:

    您可以使用query 的当前值来决定何时触发呼叫:

    $scope.$watch("query", function (value) {
    
        //implement rule here for value
        //example value is at least 3 characters
        if (value && value.length > 3) {
    
            $scope.loading = true;
            returnFactory.query($scope.query).then(function (returns) {
                $scope.returns = returns;
                $scope.loading = false;
            });
        }
    });
    

    【讨论】:

      【解决方案5】:

      只是一个我发现对类似情况有用的 sn-p:

      function watchWithDelay(scope, prop, callback, delayMs) {
        delayMs = delayMs || 1000;
        var lastTimeChanged = new Date();
        scope.$watch(prop, function(n, o) {
          lastTimeChanged = new Date();
          setTimeout(function() {
            var diff = new Date().getTime() - lastTimeChanged.getTime();
            if (diff < delayMs-100 || diff > delayMs+100) {
              return;
            }
            callback(n, o);
          }, delayMs);
        });
      }
      

      你可以像这样在控制器中使用它:

      watchWithDelay($scope, 'client.phone', function(n, o) {
        if (n === o) {
          return;
        }
        // any custom validations, for example
        if (!n) {
          return alert('Phone is required');
        }
        if (n.length < 11) {
          return alert('Phone is shorter than 11 digits');
        }
        // here I should save it somehow
        console.log('Phone is changed to ' + n);
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-04
        • 2021-01-06
        相关资源
        最近更新 更多