Implement a delay on $scope.$watch

Viewed 21250

I was wondering whether or not it is possible to implement a slight delay on $scope.$watch. I have the following which queries the server, so I'd like to implement a slight delay before it evaluates the query before querying the server. I've noticed that if you type to quickly it gets confused and doesn't send the correct information:

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

Normally i'd say use angular's $timeout for this delay but you cant clear this timeout yet.

//EDIT:you can.

Set a timeout and clear it, if this watcher gets triggered fast enought.

Like this:

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/

UPDATE Thanks to stewie this can be achieved with angular's $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);
    });

I like to use Lo-Dash which provides two really useful capabilities: debounce and throttle which does exactly what you want. Let's say you want to make sure it only calls the function once per 150 ms:

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

The throttle function lets you control when the update function is called (trailing or leading edge).

I use Lo-Dash all the time in my app. It is a must-have library for me... more useful than jQuery. But, you can create a custom build of Lo-Dash which only includes the throttle and debounce functions if you don't want to include the entire library.

You can use the current value of query to decide when you want to fire the call:

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