From f38337763bdfb4dcf9da791a48c02399e73fa60c Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Mon, 20 Nov 2017 09:40:02 +0100 Subject: [PATCH] feat(ngModelOptions): add debounce catch-all + allow debouncing 'default' only Closes #15411 BREAKING CHANGE: the 'default' key in 'debounce' now only debounces the default event, i.e. the event that is added as an update trigger by the different input directives automatically. Previously, it also applied to other update triggers defined in 'updateOn' that did not have a corresponding key in the 'debounce'. This behavior is now supported via a special wildcard / catch-all key: '*'. See the following example: Pre-1.7: 'mouseup' is also debounced by 500 milliseconds because 'default' is applied: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { 'default': 500, 'blur': 0 } } ``` 1.7: The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { '*': 500, 'blur': 0 } } ``` In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { 'default': 500 } } ``` --- src/ng/directive/ngModel.js | 6 ++++- src/ng/directive/ngModelOptions.js | 8 ++++++ test/ng/directive/ngModelOptionsSpec.js | 36 +++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index 5e9aa4a38628..8afa3da7f64a 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -838,8 +838,12 @@ NgModelController.prototype = { if (isNumber(debounceDelay[trigger])) { debounceDelay = debounceDelay[trigger]; - } else if (isNumber(debounceDelay['default'])) { + } else if (isNumber(debounceDelay['default']) && + this.$options.getOption('updateOn').indexOf(trigger) === -1 + ) { debounceDelay = debounceDelay['default']; + } else if (isNumber(debounceDelay['*'])) { + debounceDelay = debounceDelay['*']; } this.$$timeout.cancel(this.$$pendingDebounce); diff --git a/src/ng/directive/ngModelOptions.js b/src/ng/directive/ngModelOptions.js index 47c51e8460a5..2defcee0d128 100644 --- a/src/ng/directive/ngModelOptions.js +++ b/src/ng/directive/ngModelOptions.js @@ -321,6 +321,14 @@ defaultModelOptions = new ModelOptions({ * debounce: { 'default': 500, 'blur': 0 } * }" * ``` + * You can use the `*` key to specify a debounce value that applies to all events that are not + * specifically listed. In the following example, `mouseup` would have a debounce delay of 1000: + * ``` + * ng-model-options="{ + * updateOn: 'default blur mouseup', + * debounce: { 'default': 500, 'blur': 0, '*': 1000 } + * }" + * ``` * - `allowInvalid`: boolean value which indicates that the model can be set with values that did * not validate correctly instead of the default behavior of setting the model to undefined. * - `getterSetter`: boolean value which determines whether or not to treat functions bound to diff --git a/test/ng/directive/ngModelOptionsSpec.js b/test/ng/directive/ngModelOptionsSpec.js index f4142ae217ee..3814ba2bc5ca 100644 --- a/test/ng/directive/ngModelOptionsSpec.js +++ b/test/ng/directive/ngModelOptionsSpec.js @@ -498,9 +498,41 @@ describe('ngModelOptions', function() { helper.changeInputValueTo('c'); browserTrigger(helper.inputElm, 'mouseup'); - // counter-intuitively `default` in `debounce` is a catch-all + // `default` in `debounce` only affects the event triggers that are not defined in updateOn + expect($rootScope.name).toEqual('c'); + }); + + + it('should use the value of * to debounce all unspecified events', + function() { + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('a'); + expect($rootScope.name).toBeUndefined(); + $timeout.flush(6000); + expect($rootScope.name).toBeUndefined(); + $timeout.flush(4000); + expect($rootScope.name).toEqual('a'); + + helper.changeInputValueTo('b'); + browserTrigger(inputElm, 'blur'); + $timeout.flush(4000); + expect($rootScope.name).toEqual('a'); + $timeout.flush(2000); expect($rootScope.name).toEqual('b'); - $timeout.flush(10000); + + helper.changeInputValueTo('c'); + browserTrigger(helper.inputElm, 'mouseup'); + expect($rootScope.name).toEqual('b'); + $timeout.flush(10000); // flush default + expect($rootScope.name).toEqual('b'); + $timeout.flush(5000); expect($rootScope.name).toEqual('c'); });