Skip to content

Commit

Permalink
WIP: initial spike at debouncing inside NgModelController
Browse files Browse the repository at this point in the history
  • Loading branch information
petebacondarwin authored and lrlopez committed Mar 28, 2014
1 parent ff5cf73 commit c704f76
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 21 deletions.
4 changes: 3 additions & 1 deletion src/AngularPublic.js
Expand Up @@ -46,6 +46,7 @@
requiredDirective,
requiredDirective,
ngValueDirective,
ngModelOptionsDirective,
ngAttributeAliasDirectives,
ngEventDirectives,
Expand Down Expand Up @@ -183,7 +184,8 @@ function publishExternalAPI(angular){
ngChange: ngChangeDirective,
required: requiredDirective,
ngRequired: requiredDirective,
ngValue: ngValueDirective
ngValue: ngValueDirective,
ngModelOptions: ngModelOptionsDirective
}).
directive({
ngInclude: ngIncludeFillContentDirective
Expand Down
81 changes: 61 additions & 20 deletions src/ng/directive/input.js
Expand Up @@ -877,7 +877,7 @@ function addNativeHtml5Validators(ctrl, validatorName, element) {
}
}

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

function createDateInputType(type, regexp, parseDate, format) {
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
return function dynamicDateInputType(scope, element, attr, ctrl, options, $sniffer, $browser, $filter) {
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);

ctrl.$parsers.push(function(value) {
if(ctrl.$isEmpty(value)) {
Expand Down Expand Up @@ -1118,8 +1118,8 @@ function createDateInputType(type, regexp, parseDate, format) {
};
}

function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
function numberInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);

ctrl.$parsers.push(function(value) {
var empty = ctrl.$isEmpty(value);
Expand Down Expand Up @@ -1163,8 +1163,8 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
});
}

function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
function urlInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);

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

function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
function emailInputType(scope, element, attr, ctrl, options, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, options, $sniffer, $browser);

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

function radioInputType(scope, element, attr, ctrl) {
function radioInputType(scope, element, attr, ctrl, options) {
// make the name unique, if not defined
if (isUndefined(attr.name)) {
element.attr('name', nextUid());
Expand All @@ -1207,7 +1207,7 @@ function radioInputType(scope, element, attr, ctrl) {
attr.$observe('value', ctrl.$render);
}

function checkboxInputType(scope, element, attr, ctrl) {
function checkboxInputType(scope, element, attr, ctrl, options) {
var trueValue = attr.ngTrueValue,
falseValue = attr.ngFalseValue;

Expand Down Expand Up @@ -1380,10 +1380,10 @@ function checkboxInputType(scope, element, attr, ctrl) {
var inputDirective = ['$browser', '$sniffer', '$filter', function($browser, $sniffer, $filter) {
return {
restrict: 'E',
require: '?ngModel',
link: function(scope, element, attr, ctrl) {
if (ctrl) {
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
require: ['?ngModel', '^?ngModelOptions'],
link: function(scope, element, attr, ctrls) {
if (ctrls[0]) {
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], ctrls[1], $sniffer,
$browser, $filter);
}
}
Expand Down Expand Up @@ -1529,8 +1529,8 @@ var VALID_CLASS = 'ng-valid',
*
*
*/
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate',
function($scope, $exceptionHandler, $attr, $element, $parse, $animate) {
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout',
function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout) {
this.$viewValue = Number.NaN;
this.$modelValue = Number.NaN;
this.$parsers = [];
Expand All @@ -1541,9 +1541,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
this.$valid = true;
this.$invalid = false;
this.$name = $attr.name;
this.$options = { debounce: {} };


var ngModelGet = $parse($attr.ngModel),
ngModelSet = ngModelGet.assign;
ngModelSet = ngModelGet.assign,
pendingDebounce = null;

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

// change to dirty
Expand Down Expand Up @@ -1706,6 +1709,24 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
});
}
};
this.$setViewValue = function(value, trigger) {
var that = this;
trigger = trigger || 'default';
var debounceDelay = (isObject(this.$options.debounce) ? this.$options.debounce[trigger] : this.$options.debounce) || 0;

if ( pendingDebounce ) {
$timeout.cancel(pendingDebounce);
pendingDebounce = null;
}
if ( debounceDelay ) {
pendingDebounce = $timeout(function() {
pendingDebounce = null;
that.$realSetViewValue(value);
}, debounceDelay);
} else {
that.$realSetViewValue(value);
}
};

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

// Cancel any pending debounced update
if ( pendingDebounce ) {
$timeout.cancel(pendingDebounce);
pendingDebounce = null;
}

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

Expand Down Expand Up @@ -1844,7 +1871,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
*/
var ngModelDirective = function() {
return {
require: ['ngModel', '^?form'],
require: ['ngModel', '^?form', '^?ngModelOptions'],
controller: NgModelController,
link: function(scope, element, attr, ctrls) {
// notify others, especially parent forms
Expand All @@ -1854,6 +1881,11 @@ var ngModelDirective = function() {

formCtrl.$addControl(modelCtrl);

// Pass the ng-model-options to the ng-model controller
if ( ctrls[2] ) {
modelCtrl.$options = ctrls[2].$options;
}

scope.$on('$destroy', function() {
formCtrl.$removeControl(modelCtrl);
});
Expand Down Expand Up @@ -2122,3 +2154,12 @@ var ngValueDirective = function() {
}
};
};


var ngModelOptionsDirective = function() {
return {
controller: function($scope, $attrs) {
this.$options = $scope.$eval($attrs.ngModelOptions);
}
};
};

0 comments on commit c704f76

Please sign in to comment.