diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js
index d1d8e1821e67..3ea1e92887f9 100644
--- a/src/ng/directive/ngModel.js
+++ b/src/ng/directive/ngModel.js
@@ -270,6 +270,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
this.$name = $interpolate($attr.name || '', false)($scope);
this.$$parentForm = nullFormCtrl;
this.$options = defaultModelOptions;
+ this.$$updateEvents = '';
+ // Attach the correct context to the event handler function for updateOn
+ this.$$updateEventHandler = this.$$updateEventHandler.bind(this);
this.$$parsedNgModel = $parse($attr.ngModel);
this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
@@ -875,11 +878,22 @@ NgModelController.prototype = {
* See {@link ngModelOptions} for information about what options can be specified
* and how model option inheritance works.
*
+ *
+ * **Note:** this function only affects the options set on the `ngModelController`,
+ * and not the options on the {@link ngModelOptions} directive from which they might have been
+ * obtained initially.
+ *
+ *
+ *
+ * **Note:** it is not possible to override the `getterSetter` option.
+ *
+ *
* @param {Object} options a hash of settings to override the previous options
*
*/
$overrideModelOptions: function(options) {
this.$options = this.$options.createChild(options);
+ this.$$setUpdateOnEvents();
},
/**
@@ -1027,6 +1041,21 @@ NgModelController.prototype = {
this.$modelValue = this.$$rawModelValue = modelValue;
this.$$parserValid = undefined;
this.$processModelValue();
+ },
+
+ $$setUpdateOnEvents: function() {
+ if (this.$$updateEvents) {
+ this.$$element.off(this.$$updateEvents, this.$$updateEventHandler);
+ }
+
+ this.$$updateEvents = this.$options.getOption('updateOn');
+ if (this.$$updateEvents) {
+ this.$$element.on(this.$$updateEvents, this.$$updateEventHandler);
+ }
+ },
+
+ $$updateEventHandler: function(ev) {
+ this.$$debounceViewValueCommit(ev && ev.type);
}
};
@@ -1318,11 +1347,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
},
post: function ngModelPostLink(scope, element, attr, ctrls) {
var modelCtrl = ctrls[0];
- if (modelCtrl.$options.getOption('updateOn')) {
- element.on(modelCtrl.$options.getOption('updateOn'), function(ev) {
- modelCtrl.$$debounceViewValueCommit(ev && ev.type);
- });
- }
+ modelCtrl.$$setUpdateOnEvents();
function setTouched() {
modelCtrl.$setTouched();
diff --git a/src/ng/directive/ngModelOptions.js b/src/ng/directive/ngModelOptions.js
index f5d04ca0be40..93cb56af0d2f 100644
--- a/src/ng/directive/ngModelOptions.js
+++ b/src/ng/directive/ngModelOptions.js
@@ -177,6 +177,8 @@ defaultModelOptions = new ModelOptions({
* `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
* to have access to the updated model.
*
+ * ### Overriding immediate updates
+ *
* The following example shows how to override immediate updates. Changes on the inputs within the
* form will update the model only when the control loses focus (blur event). If `escape` key is
* pressed while the input field is focused, the value is reset to the value in the current model.
@@ -236,6 +238,8 @@ defaultModelOptions = new ModelOptions({
*
*
*
+ * ### Debouncing updates
+ *
* The next example shows how to debounce model changes. Model will be updated only 1 sec after last change.
* If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
*
@@ -260,6 +264,7 @@ defaultModelOptions = new ModelOptions({
*
*
*
+ *
* ## Model updates and validation
*
* The default behaviour in `ngModel` is that the model value is set to `undefined` when the
@@ -307,20 +312,41 @@ defaultModelOptions = new ModelOptions({
* You can specify the timezone that date/time input directives expect by providing its name in the
* `timezone` property.
*
+ *
+ * ## Programmatically changing options
+ *
+ * The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not
+ * watched for changes. However, it is possible to override the options on a single
+ * {@link ngModel.NgModelController} instance with
+ * {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}.
+ *
+ *
* @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
* and its descendents. Valid keys are:
* - `updateOn`: string specifying which event should the input be bound to. You can set several
* events using an space delimited list. There is a special event called `default` that
- * matches the default events belonging to the control.
+ * matches the default events belonging to the control. These are the events that are bound to
+ * the control, and when fired, update the `$viewValue` via `$setViewValue`.
+ *
+ * `ngModelOptions` considers every event that is not listed in `updateOn` a "default" event,
+ * since different control types use different default events.
+ *
+ * See also the section {@link ngModelOptions#triggering-and-debouncing-model-updates
+ * Triggering and debouncing model updates}.
+ *
* - `debounce`: integer value which contains the debounce model update value in milliseconds. A
* value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
* custom value for each event. For example:
* ```
* ng-model-options="{
- * updateOn: 'default blur',
+ * updateOn: 'default blur click',
* debounce: { 'default': 500, 'blur': 0 }
* }"
* ```
+ *
+ * "default" also applies to all events that are listed in `updateOn` but are not
+ * listed in `debounce`, i.e. "click" would also be debounced by 500 milliseconds.
+ *
* - `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..eb2b0de9993d 100644
--- a/test/ng/directive/ngModelOptionsSpec.js
+++ b/test/ng/directive/ngModelOptionsSpec.js
@@ -391,6 +391,43 @@ describe('ngModelOptions', function() {
browserTrigger(inputElm[2], 'click');
expect($rootScope.color).toBe('blue');
});
+
+ it('should re-set the trigger events when overridden with $overrideModelOptions', function() {
+ var inputElm = helper.compileInput(
+ '');
+
+ var ctrl = inputElm.controller('ngModel');
+
+ helper.changeInputValueTo('a');
+ expect($rootScope.name).toBeUndefined();
+ browserTrigger(inputElm, 'blur');
+ expect($rootScope.name).toEqual('a');
+
+ helper.changeInputValueTo('b');
+ expect($rootScope.name).toBe('a');
+ browserTrigger(inputElm, 'click');
+ expect($rootScope.name).toEqual('b');
+
+ $rootScope.$apply('name = undefined');
+ expect(inputElm.val()).toBe('');
+ ctrl.$overrideModelOptions({updateOn: 'blur mousedown'});
+
+ helper.changeInputValueTo('a');
+ expect($rootScope.name).toBeUndefined();
+ browserTrigger(inputElm, 'blur');
+ expect($rootScope.name).toEqual('a');
+
+ helper.changeInputValueTo('b');
+ expect($rootScope.name).toBe('a');
+ browserTrigger(inputElm, 'click');
+ expect($rootScope.name).toBe('a');
+
+ browserTrigger(inputElm, 'mousedown');
+ expect($rootScope.name).toEqual('b');
+ });
+
});