Debouncing Models Observable as they may be triggered too frequently and cause an InfiniteDigest Exception
Change-Id: Idaa49acc9307c93fb46b5378fa7aa1c7b201dfc6
diff --git a/src/app/core/services/helpers/debounce.helper.spec.ts b/src/app/core/services/helpers/debounce.helper.spec.ts
new file mode 100644
index 0000000..1c47dbd
--- /dev/null
+++ b/src/app/core/services/helpers/debounce.helper.spec.ts
@@ -0,0 +1,37 @@
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-ui-router';
+import {XosDebouncer, IXosDebouncer} from './debounce.helper';
+
+let service: IXosDebouncer;
+
+describe('The XosDebouncer service', () => {
+
+ beforeEach(() => {
+ angular
+ .module('test', ['toastr'])
+ .service('XosDebouncer', XosDebouncer);
+ angular.mock.module('test');
+ });
+
+ beforeEach(angular.mock.inject((
+ XosDebouncer: IXosDebouncer,
+ ) => {
+ service = XosDebouncer;
+ }));
+
+ it('should call a function only after it has not been called for 500ms', (done) => {
+ const spy = jasmine.createSpy('fn');
+ const efficientSpy = service.debounce(spy, 500, false);
+ /* tslint:disable */
+ efficientSpy();
+ efficientSpy();
+ /* tslint:enable */
+ expect(spy).not.toHaveBeenCalled();
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled();
+ done();
+ }, 600);
+ });
+});
+
diff --git a/src/app/core/services/helpers/debounce.helper.ts b/src/app/core/services/helpers/debounce.helper.ts
new file mode 100644
index 0000000..3d4011f
--- /dev/null
+++ b/src/app/core/services/helpers/debounce.helper.ts
@@ -0,0 +1,37 @@
+export interface IXosDebouncer {
+ debounce(func: any, wait: number, immediate?: boolean): any;
+}
+
+export class XosDebouncer implements IXosDebouncer {
+ static $inject = ['$log'];
+
+ constructor (
+ private $log: ng.ILogService
+ ) {
+
+ }
+
+ // wait for 'wait' ms without actions to call the function
+ // if 'immediate' call it immediatly then wait for 'wait'
+ // NOTE that we cannot use $timeout service to debounce functions as it trigger infiniteDigest Exception
+ public debounce(func: any, wait: number, immediate?: boolean) {
+ let timeout;
+ const self = this;
+ return function() {
+ const context = self;
+ const args = arguments;
+ const later = function() {
+ timeout = null;
+ if (!immediate) {
+ func.apply(context, args);
+ }
+ };
+ const callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) {
+ func.apply(context, args);
+ }
+ };
+ }
+}