Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

chore(debounce): create debounce helper #4813

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/debounce/debounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
angular.module('ui.bootstrap.debounce', [])
/**
* A helper, internal service that debounces a function
*/
.factory('$$debounce', ['$timeout', function($timeout) {
return function(callback, debounceTime) {
var timeoutPromise;

return function() {
var self = this;
var args = Array.prototype.slice(arguments);
if (timeoutPromise) {
$timeout.cancel(timeoutPromise);
}

timeoutPromise = $timeout(function() {
callback.apply(self, args);
}, debounceTime);
};
};
}]);
40 changes: 40 additions & 0 deletions src/debounce/test/debounce.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
describe('$$debounce', function() {
var $$debounce, $timeout, debouncedFunction, i;

beforeEach(module('ui.bootstrap.debounce'));
beforeEach(inject(function(_$$debounce_, _$timeout_) {
$$debounce = _$$debounce_;
$timeout = _$timeout_;
i = 0;
debouncedFunction = $$debounce(function() {
i++;
}, 100);
}));

it('should function like a $timeout when called once during timeout', function() {
debouncedFunction();
$timeout.flush(50);

expect(i).toBe(0);

$timeout.flush(50);

expect(i).toBe(1);
});

it('should only execute 100ms after last call when called twice', function() {
debouncedFunction();
$timeout.flush(50);

expect(i).toBe(0);

debouncedFunction();
$timeout.flush(50);

expect(i).toBe(0);

$timeout.flush(50);

expect(i).toBe(1);
});
});
33 changes: 14 additions & 19 deletions src/typeahead/typeahead.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])

/**
* A helper service that can parse typeahead's syntax (string provided by users)
Expand Down Expand Up @@ -26,8 +26,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
};
}])

.controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$uibPosition', 'uibTypeaheadParser',
function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) {
.controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
var HOT_KEYS = [9, 13, 27, 38, 40];
var eventDebounceTime = 200;
var modelCtrl, ngModelOptions;
Expand Down Expand Up @@ -264,8 +264,16 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
$document.find('body').bind('scroll', fireRecalculating);
}

// Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutEventPromise;
// Declare the debounced function outside recalculating for
// proper debouncing
var debouncedRecalculate = $$debounce(function() {
// if popup is visible
if (scope.matches.length) {
recalculatePosition();
}

scope.moveInProgress = false;
}, eventDebounceTime);

// Default progress type
scope.moveInProgress = false;
Expand All @@ -276,20 +284,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
scope.$digest();
}

// Cancel previous timeout
if (timeoutEventPromise) {
$timeout.cancel(timeoutEventPromise);
}

// Debounced executing recalculate after events fired
timeoutEventPromise = $timeout(function() {
// if popup is visible
if (scope.matches.length) {
recalculatePosition();
}

scope.moveInProgress = false;
}, eventDebounceTime);
debouncedRecalculate();
}

// recalculate actual position and set new values to scope
Expand Down