Skip to content

Commit c704f76

Browse files
petebacondarwinlrlopez
authored andcommitted
WIP: initial spike at debouncing inside NgModelController
1 parent ff5cf73 commit c704f76

File tree

2 files changed

+64
-21
lines changed

2 files changed

+64
-21
lines changed

src/AngularPublic.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
requiredDirective,
4747
requiredDirective,
4848
ngValueDirective,
49+
ngModelOptionsDirective,
4950
ngAttributeAliasDirectives,
5051
ngEventDirectives,
5152
@@ -183,7 +184,8 @@ function publishExternalAPI(angular){
183184
ngChange: ngChangeDirective,
184185
required: requiredDirective,
185186
ngRequired: requiredDirective,
186-
ngValue: ngValueDirective
187+
ngValue: ngValueDirective,
188+
ngModelOptions: ngModelOptionsDirective
187189
}).
188190
directive({
189191
ngInclude: ngIncludeFillContentDirective

src/ng/directive/input.js

+61-20
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ function addNativeHtml5Validators(ctrl, validatorName, element) {
877877
}
878878
}
879879

880-
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
880+
function textInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
881881
var validity = element.prop('validity');
882882
// In composition mode, users are still inputing intermediate text buffer,
883883
// hold the listener until composition is done.
@@ -1067,8 +1067,8 @@ function createDateParser(regexp, mapping) {
10671067
}
10681068

10691069
function createDateInputType(type, regexp, parseDate, format) {
1070-
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
1071-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1070+
return function dynamicDateInputType(scope, element, attr, ctrl, options, $sniffer, $browser, $filter) {
1071+
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);
10721072

10731073
ctrl.$parsers.push(function(value) {
10741074
if(ctrl.$isEmpty(value)) {
@@ -1118,8 +1118,8 @@ function createDateInputType(type, regexp, parseDate, format) {
11181118
};
11191119
}
11201120

1121-
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
1122-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1121+
function numberInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
1122+
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);
11231123

11241124
ctrl.$parsers.push(function(value) {
11251125
var empty = ctrl.$isEmpty(value);
@@ -1163,8 +1163,8 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11631163
});
11641164
}
11651165

1166-
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
1167-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1166+
function urlInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
1167+
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);
11681168

11691169
var urlValidator = function(value) {
11701170
return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
@@ -1174,8 +1174,8 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11741174
ctrl.$parsers.push(urlValidator);
11751175
}
11761176

1177-
function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
1178-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1177+
function emailInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
1178+
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);
11791179

11801180
var emailValidator = function(value) {
11811181
return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
@@ -1185,7 +1185,7 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11851185
ctrl.$parsers.push(emailValidator);
11861186
}
11871187

1188-
function radioInputType(scope, element, attr, ctrl) {
1188+
function radioInputType(scope, element, attr, ctrl, options) {
11891189
// make the name unique, if not defined
11901190
if (isUndefined(attr.name)) {
11911191
element.attr('name', nextUid());
@@ -1207,7 +1207,7 @@ function radioInputType(scope, element, attr, ctrl) {
12071207
attr.$observe('value', ctrl.$render);
12081208
}
12091209

1210-
function checkboxInputType(scope, element, attr, ctrl) {
1210+
function checkboxInputType(scope, element, attr, ctrl, options) {
12111211
var trueValue = attr.ngTrueValue,
12121212
falseValue = attr.ngFalseValue;
12131213

@@ -1380,10 +1380,10 @@ function checkboxInputType(scope, element, attr, ctrl) {
13801380
var inputDirective = ['$browser', '$sniffer', '$filter', function($browser, $sniffer, $filter) {
13811381
return {
13821382
restrict: 'E',
1383-
require: '?ngModel',
1384-
link: function(scope, element, attr, ctrl) {
1385-
if (ctrl) {
1386-
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
1383+
require: ['?ngModel', '^?ngModelOptions'],
1384+
link: function(scope, element, attr, ctrls) {
1385+
if (ctrls[0]) {
1386+
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], ctrls[1], $sniffer,
13871387
$browser, $filter);
13881388
}
13891389
}
@@ -1529,8 +1529,8 @@ var VALID_CLASS = 'ng-valid',
15291529
*
15301530
*
15311531
*/
1532-
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate',
1533-
function($scope, $exceptionHandler, $attr, $element, $parse, $animate) {
1532+
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout',
1533+
function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout) {
15341534
this.$viewValue = Number.NaN;
15351535
this.$modelValue = Number.NaN;
15361536
this.$parsers = [];
@@ -1541,9 +1541,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
15411541
this.$valid = true;
15421542
this.$invalid = false;
15431543
this.$name = $attr.name;
1544+
this.$options = { debounce: {} };
1545+
15441546

15451547
var ngModelGet = $parse($attr.ngModel),
1546-
ngModelSet = ngModelGet.assign;
1548+
ngModelSet = ngModelGet.assign,
1549+
pendingDebounce = null;
15471550

15481551
if (!ngModelSet) {
15491552
throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
@@ -1678,7 +1681,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
16781681
*
16791682
* @param {string} value Value from the view.
16801683
*/
1681-
this.$setViewValue = function(value) {
1684+
this.$realSetViewValue = function(value) {
16821685
this.$viewValue = value;
16831686

16841687
// change to dirty
@@ -1706,6 +1709,24 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17061709
});
17071710
}
17081711
};
1712+
this.$setViewValue = function(value, trigger) {
1713+
var that = this;
1714+
trigger = trigger || 'default';
1715+
var debounceDelay = (isObject(this.$options.debounce) ? this.$options.debounce[trigger] : this.$options.debounce) || 0;
1716+
1717+
if ( pendingDebounce ) {
1718+
$timeout.cancel(pendingDebounce);
1719+
pendingDebounce = null;
1720+
}
1721+
if ( debounceDelay ) {
1722+
pendingDebounce = $timeout(function() {
1723+
pendingDebounce = null;
1724+
that.$realSetViewValue(value);
1725+
}, debounceDelay);
1726+
} else {
1727+
that.$realSetViewValue(value);
1728+
}
1729+
};
17091730

17101731
// model -> value
17111732
var ctrl = this;
@@ -1716,6 +1737,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17161737
// if scope model value and ngModel value are out of sync
17171738
if (ctrl.$modelValue !== value) {
17181739

1740+
// Cancel any pending debounced update
1741+
if ( pendingDebounce ) {
1742+
$timeout.cancel(pendingDebounce);
1743+
pendingDebounce = null;
1744+
}
1745+
17191746
var formatters = ctrl.$formatters,
17201747
idx = formatters.length;
17211748

@@ -1844,7 +1871,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18441871
*/
18451872
var ngModelDirective = function() {
18461873
return {
1847-
require: ['ngModel', '^?form'],
1874+
require: ['ngModel', '^?form', '^?ngModelOptions'],
18481875
controller: NgModelController,
18491876
link: function(scope, element, attr, ctrls) {
18501877
// notify others, especially parent forms
@@ -1854,6 +1881,11 @@ var ngModelDirective = function() {
18541881

18551882
formCtrl.$addControl(modelCtrl);
18561883

1884+
// Pass the ng-model-options to the ng-model controller
1885+
if ( ctrls[2] ) {
1886+
modelCtrl.$options = ctrls[2].$options;
1887+
}
1888+
18571889
scope.$on('$destroy', function() {
18581890
formCtrl.$removeControl(modelCtrl);
18591891
});
@@ -2122,3 +2154,12 @@ var ngValueDirective = function() {
21222154
}
21232155
};
21242156
};
2157+
2158+
2159+
var ngModelOptionsDirective = function() {
2160+
return {
2161+
controller: function($scope, $attrs) {
2162+
this.$options = $scope.$eval($attrs.ngModelOptions);
2163+
}
2164+
};
2165+
};

0 commit comments

Comments
 (0)