8
8
TOUCHED_CLASS: true,
9
9
PENDING_CLASS: true,
10
10
addSetValidityMethod: true,
11
- setupValidity: true
11
+ setupValidity: true,
12
+ $defaultModelOptions: false
12
13
*/
13
14
15
+
14
16
var VALID_CLASS = 'ng-valid' ,
15
17
INVALID_CLASS = 'ng-invalid' ,
16
18
PRISTINE_CLASS = 'ng-pristine' ,
@@ -243,6 +245,7 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
243
245
this . $pending = undefined ; // keep pending keys here
244
246
this . $name = $interpolate ( $attr . name || '' , false ) ( $scope ) ;
245
247
this . $$parentForm = nullFormCtrl ;
248
+ this . $options = $defaultModelOptions ;
246
249
247
250
this . $$parsedNgModel = $parse ( $attr . ngModel ) ;
248
251
this . $$parsedNgModelAssign = this . $$parsedNgModel . assign ;
@@ -267,9 +270,8 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
267
270
}
268
271
269
272
NgModelController . prototype = {
270
- $$setOptions : function ( options ) {
271
- this . $options = options ;
272
- if ( options && options . getterSetter ) {
273
+ $$initGetterSetters : function ( ) {
274
+ if ( this . $options . getOption ( 'getterSetter' ) ) {
273
275
var invokeModelGetter = this . $$parse ( this . $$attr . ngModel + '()' ) ,
274
276
invokeModelSetter = this . $$parse ( this . $$attr . ngModel + '($$$p)' ) ;
275
277
@@ -543,7 +545,7 @@ NgModelController.prototype = {
543
545
var prevValid = this . $valid ;
544
546
var prevModelValue = this . $modelValue ;
545
547
546
- var allowInvalid = this . $options && this . $options . allowInvalid ;
548
+ var allowInvalid = this . $options . getOption ( ' allowInvalid' ) ;
547
549
548
550
var that = this ;
549
551
this . $$runValidators ( modelValue , viewValue , function ( allValid ) {
@@ -708,7 +710,7 @@ NgModelController.prototype = {
708
710
this . $modelValue = this . $$ngModelGet ( this . $$scope ) ;
709
711
}
710
712
var prevModelValue = this . $modelValue ;
711
- var allowInvalid = this . $options && this . $options . allowInvalid ;
713
+ var allowInvalid = this . $options . getOption ( ' allowInvalid' ) ;
712
714
this . $$rawModelValue = modelValue ;
713
715
714
716
if ( allowInvalid ) {
@@ -800,25 +802,18 @@ NgModelController.prototype = {
800
802
*/
801
803
$setViewValue : function ( value , trigger ) {
802
804
this . $viewValue = value ;
803
- if ( ! this . $options || this . $options . updateOnDefault ) {
805
+ if ( this . $options . getOption ( ' updateOnDefault' ) ) {
804
806
this . $$debounceViewValueCommit ( trigger ) ;
805
807
}
806
808
} ,
807
809
808
810
$$debounceViewValueCommit : function ( trigger ) {
809
- var debounceDelay = 0 ,
810
- options = this . $options ,
811
- debounce ;
812
-
813
- if ( options && isDefined ( options . debounce ) ) {
814
- debounce = options . debounce ;
815
- if ( isNumber ( debounce ) ) {
816
- debounceDelay = debounce ;
817
- } else if ( isNumber ( debounce [ trigger ] ) ) {
818
- debounceDelay = debounce [ trigger ] ;
819
- } else if ( isNumber ( debounce [ 'default' ] ) ) {
820
- debounceDelay = debounce [ 'default' ] ;
821
- }
811
+ var debounceDelay = this . $options . getOption ( 'debounce' ) ;
812
+
813
+ if ( isNumber ( debounceDelay [ trigger ] ) ) {
814
+ debounceDelay = debounceDelay [ trigger ] ;
815
+ } else if ( isNumber ( debounceDelay [ 'default' ] ) ) {
816
+ debounceDelay = debounceDelay [ 'default' ] ;
822
817
}
823
818
824
819
this . $$timeout . cancel ( this . $$pendingDebounce ) ;
@@ -1116,9 +1111,14 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
1116
1111
return {
1117
1112
pre : function ngModelPreLink ( scope , element , attr , ctrls ) {
1118
1113
var modelCtrl = ctrls [ 0 ] ,
1119
- formCtrl = ctrls [ 1 ] || modelCtrl . $$parentForm ;
1114
+ formCtrl = ctrls [ 1 ] || modelCtrl . $$parentForm ,
1115
+ optionsCtrl = ctrls [ 2 ] ;
1116
+
1117
+ if ( optionsCtrl ) {
1118
+ modelCtrl . $options = optionsCtrl . $options ;
1119
+ }
1120
1120
1121
- modelCtrl . $$setOptions ( ctrls [ 2 ] && ctrls [ 2 ] . $options ) ;
1121
+ modelCtrl . $$initGetterSetters ( ) ;
1122
1122
1123
1123
// notify others, especially parent forms
1124
1124
formCtrl . $addControl ( modelCtrl ) ;
@@ -1135,8 +1135,8 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
1135
1135
} ,
1136
1136
post : function ngModelPostLink ( scope , element , attr , ctrls ) {
1137
1137
var modelCtrl = ctrls [ 0 ] ;
1138
- if ( modelCtrl . $options && modelCtrl . $options . updateOn ) {
1139
- element . on ( modelCtrl . $options . updateOn , function ( ev ) {
1138
+ if ( modelCtrl . $options . getOption ( ' updateOn' ) ) {
1139
+ element . on ( modelCtrl . $options . getOption ( ' updateOn' ) , function ( ev ) {
1140
1140
modelCtrl . $$debounceViewValueCommit ( ev && ev . type ) ;
1141
1141
} ) ;
1142
1142
}
@@ -1159,189 +1159,3 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
1159
1159
}
1160
1160
} ;
1161
1161
} ] ;
1162
-
1163
-
1164
-
1165
- var DEFAULT_REGEXP = / ( \s + | ^ ) d e f a u l t ( \s + | $ ) / ;
1166
-
1167
- /**
1168
- * @ngdoc directive
1169
- * @name ngModelOptions
1170
- *
1171
- * @description
1172
- * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
1173
- * events that will trigger a model update and/or a debouncing delay so that the actual update only
1174
- * takes place when a timer expires; this timer will be reset after another change takes place.
1175
- *
1176
- * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
1177
- * be different from the value in the actual model. This means that if you update the model you
1178
- * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
1179
- * order to make sure it is synchronized with the model and that any debounced action is canceled.
1180
- *
1181
- * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
1182
- * method is by making sure the input is placed inside a form that has a `name` attribute. This is
1183
- * important because `form` controllers are published to the related scope under the name in their
1184
- * `name` attribute.
1185
- *
1186
- * Any pending changes will take place immediately when an enclosing form is submitted via the
1187
- * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
1188
- * to have access to the updated model.
1189
- *
1190
- * `ngModelOptions` has an effect on the element it's declared on and its descendants.
1191
- *
1192
- * @param {Object } ngModelOptions options to apply to the current model. Valid keys are:
1193
- * - `updateOn`: string specifying which event should the input be bound to. You can set several
1194
- * events using an space delimited list. There is a special event called `default` that
1195
- * matches the default events belonging to the control.
1196
- * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
1197
- * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
1198
- * custom value for each event. For example:
1199
- * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
1200
- * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
1201
- * not validate correctly instead of the default behavior of setting the model to undefined.
1202
- * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
1203
- `ngModel` as getters/setters.
1204
- * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
1205
- * `<input type="date" />`, `<input type="time" />`, ... . It understands UTC/GMT and the
1206
- * continental US time zone abbreviations, but for general use, use a time zone offset, for
1207
- * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
1208
- * If not specified, the timezone of the browser will be used.
1209
- *
1210
- * @example
1211
-
1212
- The following example shows how to override immediate updates. Changes on the inputs within the
1213
- form will update the model only when the control loses focus (blur event). If `escape` key is
1214
- pressed while the input field is focused, the value is reset to the value in the current model.
1215
-
1216
- <example name="ngModelOptions-directive-blur" module="optionsExample">
1217
- <file name="index.html">
1218
- <div ng-controller="ExampleController">
1219
- <form name="userForm">
1220
- <label>Name:
1221
- <input type="text" name="userName"
1222
- ng-model="user.name"
1223
- ng-model-options="{ updateOn: 'blur' }"
1224
- ng-keyup="cancel($event)" />
1225
- </label><br />
1226
- <label>Other data:
1227
- <input type="text" ng-model="user.data" />
1228
- </label><br />
1229
- </form>
1230
- <pre>user.name = <span ng-bind="user.name"></span></pre>
1231
- <pre>user.data = <span ng-bind="user.data"></span></pre>
1232
- </div>
1233
- </file>
1234
- <file name="app.js">
1235
- angular.module('optionsExample', [])
1236
- .controller('ExampleController', ['$scope', function($scope) {
1237
- $scope.user = { name: 'John', data: '' };
1238
-
1239
- $scope.cancel = function(e) {
1240
- if (e.keyCode === 27) {
1241
- $scope.userForm.userName.$rollbackViewValue();
1242
- }
1243
- };
1244
- }]);
1245
- </file>
1246
- <file name="protractor.js" type="protractor">
1247
- var model = element(by.binding('user.name'));
1248
- var input = element(by.model('user.name'));
1249
- var other = element(by.model('user.data'));
1250
-
1251
- it('should allow custom events', function() {
1252
- input.sendKeys(' Doe');
1253
- input.click();
1254
- expect(model.getText()).toEqual('John');
1255
- other.click();
1256
- expect(model.getText()).toEqual('John Doe');
1257
- });
1258
-
1259
- it('should $rollbackViewValue when model changes', function() {
1260
- input.sendKeys(' Doe');
1261
- expect(input.getAttribute('value')).toEqual('John Doe');
1262
- input.sendKeys(protractor.Key.ESCAPE);
1263
- expect(input.getAttribute('value')).toEqual('John');
1264
- other.click();
1265
- expect(model.getText()).toEqual('John');
1266
- });
1267
- </file>
1268
- </example>
1269
-
1270
- This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
1271
- If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
1272
-
1273
- <example name="ngModelOptions-directive-debounce" module="optionsExample">
1274
- <file name="index.html">
1275
- <div ng-controller="ExampleController">
1276
- <form name="userForm">
1277
- <label>Name:
1278
- <input type="text" name="userName"
1279
- ng-model="user.name"
1280
- ng-model-options="{ debounce: 1000 }" />
1281
- </label>
1282
- <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
1283
- <br />
1284
- </form>
1285
- <pre>user.name = <span ng-bind="user.name"></span></pre>
1286
- </div>
1287
- </file>
1288
- <file name="app.js">
1289
- angular.module('optionsExample', [])
1290
- .controller('ExampleController', ['$scope', function($scope) {
1291
- $scope.user = { name: 'Igor' };
1292
- }]);
1293
- </file>
1294
- </example>
1295
-
1296
- This one shows how to bind to getter/setters:
1297
-
1298
- <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
1299
- <file name="index.html">
1300
- <div ng-controller="ExampleController">
1301
- <form name="userForm">
1302
- <label>Name:
1303
- <input type="text" name="userName"
1304
- ng-model="user.name"
1305
- ng-model-options="{ getterSetter: true }" />
1306
- </label>
1307
- </form>
1308
- <pre>user.name = <span ng-bind="user.name()"></span></pre>
1309
- </div>
1310
- </file>
1311
- <file name="app.js">
1312
- angular.module('getterSetterExample', [])
1313
- .controller('ExampleController', ['$scope', function($scope) {
1314
- var _name = 'Brian';
1315
- $scope.user = {
1316
- name: function(newName) {
1317
- // Note that newName can be undefined for two reasons:
1318
- // 1. Because it is called as a getter and thus called with no arguments
1319
- // 2. Because the property should actually be set to undefined. This happens e.g. if the
1320
- // input is invalid
1321
- return arguments.length ? (_name = newName) : _name;
1322
- }
1323
- };
1324
- }]);
1325
- </file>
1326
- </example>
1327
- */
1328
- var ngModelOptionsDirective = function ( ) {
1329
- return {
1330
- restrict : 'A' ,
1331
- controller : [ '$scope' , '$attrs' , function NgModelOptionsController ( $scope , $attrs ) {
1332
- var that = this ;
1333
- this . $options = copy ( $scope . $eval ( $attrs . ngModelOptions ) ) ;
1334
- // Allow adding/overriding bound events
1335
- if ( isDefined ( this . $options . updateOn ) ) {
1336
- this . $options . updateOnDefault = false ;
1337
- // extract "default" pseudo-event from list of events that can trigger a model update
1338
- this . $options . updateOn = trim ( this . $options . updateOn . replace ( DEFAULT_REGEXP , function ( ) {
1339
- that . $options . updateOnDefault = true ;
1340
- return ' ' ;
1341
- } ) ) ;
1342
- } else {
1343
- this . $options . updateOnDefault = true ;
1344
- }
1345
- } ]
1346
- } ;
1347
- } ;
0 commit comments