Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix(ng-model-options): Canceling debounces now forces a view reset #7011

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: 19 additions & 2 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -1701,10 +1701,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* This method should be called before directly update a debounced model from the scope in
* order to prevent unintended future changes of the model value because of a delayed event.
*/
this.$cancelDebounce = function() {
this.$cancelDebounce = function(skipRender) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we should rename this skipReset

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I'll also document the new parameter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about skipViewValReset to make it more explicit?

if ( pendingDebounce ) {
$timeout.cancel(pendingDebounce);
pendingDebounce = null;
if ( !skipRender ) {
this.$resetModelValue();
}
}
};

Expand Down Expand Up @@ -1769,7 +1772,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
? (this.$options.debounce[trigger] || this.$options.debounce['default'] || 0)
: this.$options.debounce) || 0;

that.$cancelDebounce();
that.$cancelDebounce(true);
if ( debounceDelay ) {
pendingDebounce = $timeout(function() {
pendingDebounce = null;
Expand All @@ -1783,6 +1786,20 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
// model -> value
var ctrl = this;

this.$resetModelValue = function() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$resetView code shares a lot with ngModelWatch. In fact the only difference is that ngModelWatch only updates $viewValue and calls $render when there is an actual change. We could add a parameter to $resetView which could allow enabling the check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was wondering about that. Something like like forceRender?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just call this fn from ngModelWatch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ngModelWatch is a bit different since it tests that the $modelValue and $viewValue actually changed (this fn is forcing it). We could add a force param here and call this from ngModelWatch. But I was wondering if it is even needed to re-run the $formatters, we could just invoke $render...

var formatters = ctrl.$formatters,
idx = formatters.length;

var value = ngModelGet($scope);
ctrl.$modelValue = value;
while(idx--) {
value = formatters[idx](value);
}

ctrl.$viewValue = value;
ctrl.$render();
};

$scope.$watch(function ngModelWatch() {
var value = ngModelGet($scope);

Expand Down
15 changes: 15 additions & 0 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,21 @@ describe('input', function() {
expect(scope.name).toEqual(undefined);
}));


it("should reset the input value when cancelDebounce is called", inject(function($timeout) {
compileInput(
'<form name="test">'+
'<input type="text" ng-model="name" name="alias" '+
'ng-model-options="{ debounce: 2000 }" />'+
'</form>');

inputElm.val('a');
scope.test.alias.$cancelDebounce();
expect(inputElm.val()).toBe('');
$timeout.flush(3000);
expect(inputElm.val()).toBe('');
}));

});

it('should allow complex reference binding', function() {
Expand Down