88 TOUCHED_CLASS: true,
99 PENDING_CLASS: true,
1010 addSetValidityMethod: true,
11- setupValidity: true
11+ setupValidity: true,
12+ $defaultModelOptions: false
1213*/
1314
15+
1416var VALID_CLASS = 'ng-valid' ,
1517 INVALID_CLASS = 'ng-invalid' ,
1618 PRISTINE_CLASS = 'ng-pristine' ,
@@ -243,6 +245,7 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
243245 this . $pending = undefined ; // keep pending keys here
244246 this . $name = $interpolate ( $attr . name || '' , false ) ( $scope ) ;
245247 this . $$parentForm = nullFormCtrl ;
248+ this . $options = $defaultModelOptions ;
246249
247250 this . $$parsedNgModel = $parse ( $attr . ngModel ) ;
248251 this . $$parsedNgModelAssign = this . $$parsedNgModel . assign ;
@@ -267,9 +270,8 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
267270}
268271
269272NgModelController . prototype = {
270- $$setOptions : function ( options ) {
271- this . $options = options ;
272- if ( options && options . getterSetter ) {
273+ $$initGetterSetters : function ( ) {
274+ if ( this . $options . getOption ( 'getterSetter' ) ) {
273275 var invokeModelGetter = this . $$parse ( this . $$attr . ngModel + '()' ) ,
274276 invokeModelSetter = this . $$parse ( this . $$attr . ngModel + '($$$p)' ) ;
275277
@@ -543,7 +545,7 @@ NgModelController.prototype = {
543545 var prevValid = this . $valid ;
544546 var prevModelValue = this . $modelValue ;
545547
546- var allowInvalid = this . $options && this . $options . allowInvalid ;
548+ var allowInvalid = this . $options . getOption ( ' allowInvalid' ) ;
547549
548550 var that = this ;
549551 this . $$runValidators ( modelValue , viewValue , function ( allValid ) {
@@ -708,7 +710,7 @@ NgModelController.prototype = {
708710 this . $modelValue = this . $$ngModelGet ( this . $$scope ) ;
709711 }
710712 var prevModelValue = this . $modelValue ;
711- var allowInvalid = this . $options && this . $options . allowInvalid ;
713+ var allowInvalid = this . $options . getOption ( ' allowInvalid' ) ;
712714 this . $$rawModelValue = modelValue ;
713715
714716 if ( allowInvalid ) {
@@ -800,25 +802,18 @@ NgModelController.prototype = {
800802 */
801803 $setViewValue : function ( value , trigger ) {
802804 this . $viewValue = value ;
803- if ( ! this . $options || this . $options . updateOnDefault ) {
805+ if ( this . $options . getOption ( ' updateOnDefault' ) ) {
804806 this . $$debounceViewValueCommit ( trigger ) ;
805807 }
806808 } ,
807809
808810 $$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' ] ;
822817 }
823818
824819 this . $$timeout . cancel ( this . $$pendingDebounce ) ;
@@ -1116,9 +1111,14 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
11161111 return {
11171112 pre : function ngModelPreLink ( scope , element , attr , ctrls ) {
11181113 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+ }
11201120
1121- modelCtrl . $$setOptions ( ctrls [ 2 ] && ctrls [ 2 ] . $options ) ;
1121+ modelCtrl . $$initGetterSetters ( ) ;
11221122
11231123 // notify others, especially parent forms
11241124 formCtrl . $addControl ( modelCtrl ) ;
@@ -1135,8 +1135,8 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
11351135 } ,
11361136 post : function ngModelPostLink ( scope , element , attr , ctrls ) {
11371137 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 ) {
11401140 modelCtrl . $$debounceViewValueCommit ( ev && ev . type ) ;
11411141 } ) ;
11421142 }
@@ -1159,189 +1159,3 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
11591159 }
11601160 } ;
11611161} ] ;
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