This repository was archived by the owner on Apr 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 27.4k
/
Copy pathform.js
727 lines (675 loc) · 26.2 KB
/
form.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
'use strict';
/* global -nullFormCtrl, -PENDING_CLASS, -SUBMITTED_CLASS
*/
var nullFormCtrl = {
$addControl: noop,
$getControls: valueFn([]),
$$renameControl: nullFormRenameControl,
$removeControl: noop,
$setValidity: noop,
$setDirty: noop,
$setPristine: noop,
$setSubmitted: noop,
$$setSubmitted: noop
},
PENDING_CLASS = 'ng-pending',
SUBMITTED_CLASS = 'ng-submitted';
function nullFormRenameControl(control, name) {
control.$name = name;
}
/**
* @ngdoc type
* @name form.FormController
*
* @property {boolean} $pristine True if user has not interacted with the form yet.
* @property {boolean} $dirty True if user has already interacted with the form.
* @property {boolean} $valid True if all of the containing forms and controls are valid.
* @property {boolean} $invalid True if at least one containing control or form is invalid.
* @property {boolean} $submitted True if user has submitted the form even if its invalid.
*
* @property {Object} $pending An object hash, containing references to controls or forms with
* pending validators, where:
*
* - keys are validations tokens (error names).
* - values are arrays of controls or forms that have a pending validator for the given error name.
*
* See {@link form.FormController#$error $error} for a list of built-in validation tokens.
*
* @property {Object} $error An object hash, containing references to controls or forms with failing
* validators, where:
*
* - keys are validation tokens (error names),
* - values are arrays of controls or forms that have a failing validator for the given error name.
*
* Built-in validation tokens:
* - `email`
* - `max`
* - `maxlength`
* - `min`
* - `minlength`
* - `number`
* - `pattern`
* - `required`
* - `url`
* - `date`
* - `datetimelocal`
* - `time`
* - `week`
* - `month`
*
* @description
* `FormController` keeps track of all its controls and nested forms as well as the state of them,
* such as being valid/invalid or dirty/pristine.
*
* Each {@link ng.directive:form form} directive creates an instance
* of `FormController`.
*
*/
//asks for $scope to fool the BC controller module
FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
function FormController($element, $attrs, $scope, $animate, $interpolate) {
this.$$controls = [];
// init state
this.$error = {};
this.$$success = {};
this.$pending = undefined;
this.$name = $interpolate($attrs.name || $attrs.ngForm || '')($scope);
this.$dirty = false;
this.$pristine = true;
this.$valid = true;
this.$invalid = false;
this.$submitted = false;
this.$$parentForm = nullFormCtrl;
this.$$element = $element;
this.$$animate = $animate;
setupValidity(this);
}
FormController.prototype = {
/**
* @ngdoc method
* @name form.FormController#$rollbackViewValue
*
* @description
* Rollback all form controls pending updates to the `$modelValue`.
*
* Updates may be pending by a debounced event or because the input is waiting for a some future
* event defined in `ng-model-options`. This method is typically needed by the reset button of
* a form that uses `ng-model-options` to pend updates.
*/
$rollbackViewValue: function() {
forEach(this.$$controls, function(control) {
control.$rollbackViewValue();
});
},
/**
* @ngdoc method
* @name form.FormController#$commitViewValue
*
* @description
* Commit all form controls pending updates to the `$modelValue`.
*
* Updates may be pending by a debounced event or because the input is waiting for a some future
* event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
* usually handles calling this in response to input events.
*/
$commitViewValue: function() {
forEach(this.$$controls, function(control) {
control.$commitViewValue();
});
},
/**
* @ngdoc method
* @name form.FormController#$addControl
* @param {object} control control object, either a {@link form.FormController} or an
* {@link ngModel.NgModelController}
*
* @description
* Register a control with the form. Input elements using ngModelController do this automatically
* when they are linked.
*
* Note that the current state of the control will not be reflected on the new parent form. This
* is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
* state.
*
* However, if the method is used programmatically, for example by adding dynamically created controls,
* or controls that have been previously removed without destroying their corresponding DOM element,
* it's the developers responsibility to make sure the current state propagates to the parent form.
*
* For example, if an input control is added that is already `$dirty` and has `$error` properties,
* calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
*/
$addControl: function(control) {
// Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
// and not added to the scope. Now we throw an error.
assertNotHasOwnProperty(control.$name, 'input');
this.$$controls.push(control);
if (control.$name) {
this[control.$name] = control;
}
control.$$parentForm = this;
},
/**
* @ngdoc method
* @name form.FormController#$getControls
* @returns {Array} the controls that are currently part of this form
*
* @description
* This method returns a **shallow copy** of the controls that are currently part of this form.
* The controls can be instances of {@link form.FormController `FormController`}
* ({@link ngForm "child-forms"}) and of {@link ngModel.NgModelController `NgModelController`}.
* If you need access to the controls of child-forms, you have to call `$getControls()`
* recursively on them.
* This can be used for example to iterate over all controls to validate them.
*
* The controls can be accessed normally, but adding to, or removing controls from the array has
* no effect on the form. Instead, use {@link form.FormController#$addControl `$addControl()`} and
* {@link form.FormController#$removeControl `$removeControl()`} for this use-case.
* Likewise, adding a control to, or removing a control from the form is not reflected
* in the shallow copy. That means you should get a fresh copy from `$getControls()` every time
* you need access to the controls.
*/
$getControls: function() {
return shallowCopy(this.$$controls);
},
// Private API: rename a form control
$$renameControl: function(control, newName) {
var oldName = control.$name;
if (this[oldName] === control) {
delete this[oldName];
}
this[newName] = control;
control.$name = newName;
},
/**
* @ngdoc method
* @name form.FormController#$removeControl
* @param {object} control control object, either a {@link form.FormController} or an
* {@link ngModel.NgModelController}
*
* @description
* Deregister a control from the form.
*
* Input elements using ngModelController do this automatically when they are destroyed.
*
* Note that only the removed control's validation state (`$errors`etc.) will be removed from the
* form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
* different from case to case. For example, removing the only `$dirty` control from a form may or
* may not mean that the form is still `$dirty`.
*/
$removeControl: function(control) {
if (control.$name && this[control.$name] === control) {
delete this[control.$name];
}
forEach(this.$pending, function(value, name) {
// eslint-disable-next-line no-invalid-this
this.$setValidity(name, null, control);
}, this);
forEach(this.$error, function(value, name) {
// eslint-disable-next-line no-invalid-this
this.$setValidity(name, null, control);
}, this);
forEach(this.$$success, function(value, name) {
// eslint-disable-next-line no-invalid-this
this.$setValidity(name, null, control);
}, this);
arrayRemove(this.$$controls, control);
control.$$parentForm = nullFormCtrl;
},
/**
* @ngdoc method
* @name form.FormController#$setDirty
*
* @description
* Sets the form to a dirty state.
*
* This method can be called to add the 'ng-dirty' class and set the form to a dirty
* state (ng-dirty class). This method will also propagate to parent forms.
*/
$setDirty: function() {
this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
this.$$animate.addClass(this.$$element, DIRTY_CLASS);
this.$dirty = true;
this.$pristine = false;
this.$$parentForm.$setDirty();
},
/**
* @ngdoc method
* @name form.FormController#$setPristine
*
* @description
* Sets the form to its pristine state.
*
* This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes
* the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted`
* state to false.
*
* This method will also propagate to all the controls contained in this form.
*
* Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
* saving or resetting it.
*/
$setPristine: function() {
this.$$animate.setClass(this.$$element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
this.$dirty = false;
this.$pristine = true;
this.$submitted = false;
forEach(this.$$controls, function(control) {
control.$setPristine();
});
},
/**
* @ngdoc method
* @name form.FormController#$setUntouched
*
* @description
* Sets the form to its untouched state.
*
* This method can be called to remove the 'ng-touched' class and set the form controls to their
* untouched state (ng-untouched class).
*
* Setting a form controls back to their untouched state is often useful when setting the form
* back to its pristine state.
*/
$setUntouched: function() {
forEach(this.$$controls, function(control) {
control.$setUntouched();
});
},
/**
* @ngdoc method
* @name form.FormController#$setSubmitted
*
* @description
* Sets the form to its `$submitted` state. This will also set `$submitted` on all child and
* parent forms of the form.
*/
$setSubmitted: function() {
var rootForm = this;
while (rootForm.$$parentForm && (rootForm.$$parentForm !== nullFormCtrl)) {
rootForm = rootForm.$$parentForm;
}
rootForm.$$setSubmitted();
},
$$setSubmitted: function() {
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
this.$submitted = true;
forEach(this.$$controls, function(control) {
if (control.$$setSubmitted) {
control.$$setSubmitted();
}
});
}
};
/**
* @ngdoc method
* @name form.FormController#$setValidity
*
* @description
* Change the validity state of the form, and notify the parent form (if any).
*
* Application developers will rarely need to call this method directly. It is used internally, by
* {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a
* control's validity state to the parent `FormController`.
*
* @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be
* assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for
* unfulfilled `$asyncValidators`), so that it is available for data-binding. The
* `validationErrorKey` should be in camelCase and will get converted into dash-case for
* class name. Example: `myError` will result in `ng-valid-my-error` and
* `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`.
* @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending
* (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
* Skipped is used by AngularJS when validators do not run because of parse errors and when
* `$asyncValidators` do not run because any of the `$validators` failed.
* @param {NgModelController | FormController} controller - The controller whose validity state is
* triggering the change.
*/
addSetValidityMethod({
clazz: FormController,
set: function(object, property, controller) {
var list = object[property];
if (!list) {
object[property] = [controller];
} else {
var index = list.indexOf(controller);
if (index === -1) {
list.push(controller);
}
}
},
unset: function(object, property, controller) {
var list = object[property];
if (!list) {
return;
}
arrayRemove(list, controller);
if (list.length === 0) {
delete object[property];
}
}
});
/**
* @ngdoc directive
* @name ngForm
* @restrict EAC
*
* @description
* Helper directive that makes it possible to create control groups inside a
* {@link ng.directive:form `form`} directive.
* These "child forms" can be used, for example, to determine the validity of a sub-group of
* controls.
*
* <div class="alert alert-danger">
* **Note**: `ngForm` cannot be used as a replacement for `<form>`, because it lacks its
* [built-in HTML functionality](https://html.spec.whatwg.org/#the-form-element).
* Specifically, you cannot submit `ngForm` like a `<form>` tag. That means,
* you cannot send data to the server with `ngForm`, or integrate it with
* {@link ng.directive:ngSubmit `ngSubmit`}.
* </div>
*
* @param {string=} ngForm|name Name of the form. If specified, the form controller will
* be published into the related scope, under this name.
*
*/
/**
* @ngdoc directive
* @name form
* @restrict E
*
* @description
* Directive that instantiates
* {@link form.FormController FormController}.
*
* If the `name` attribute is specified, the form controller is published onto the current scope under
* this name.
*
* ## Alias: {@link ng.directive:ngForm `ngForm`}
*
* In AngularJS, forms can be nested. This means that the outer form is valid when all of the child
* forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
* AngularJS provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
* `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
* of controls needs to be determined.
*
* ## CSS classes
* - `ng-valid` is set if the form is valid.
* - `ng-invalid` is set if the form is invalid.
* - `ng-pending` is set if the form is pending.
* - `ng-pristine` is set if the form is pristine.
* - `ng-dirty` is set if the form is dirty.
* - `ng-submitted` is set if the form was submitted.
*
* Keep in mind that ngAnimate can detect each of these classes when added and removed.
*
*
* ## Submitting a form and preventing the default action
*
* Since the role of forms in client-side AngularJS applications is different than in classical
* roundtrip apps, it is desirable for the browser not to translate the form submission into a full
* page reload that sends the data to the server. Instead some javascript logic should be triggered
* to handle the form submission in an application-specific way.
*
* For this reason, AngularJS prevents the default action (form submission to the server) unless the
* `<form>` element has an `action` attribute specified.
*
* You can use one of the following two ways to specify what javascript method should be called when
* a form is submitted:
*
* - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
* - {@link ng.directive:ngClick ngClick} directive on the first
* button or input field of type submit (input[type=submit])
*
* To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
* or {@link ng.directive:ngClick ngClick} directives.
* This is because of the following form submission rules in the HTML specification:
*
* - If a form has only one input field then hitting enter in this field triggers form submit
* (`ngSubmit`)
* - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
* doesn't trigger submit
* - if a form has one or more input fields and one or more buttons or input[type=submit] then
* hitting enter in any of the input fields will trigger the click handler on the *first* button or
* input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
*
* Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
* submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
* to have access to the updated model.
*
* @animations
* Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
* These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
* other validations that are performed within the form. Animations in ngForm are similar to how
* they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
* as JS animations.
*
* The following example shows a simple way to utilize CSS transitions to style a form element
* that has been rendered as invalid after it has been validated:
*
* <pre>
* //be sure to include ngAnimate as a module to hook into more
* //advanced animations
* .my-form {
* transition:0.5s linear all;
* background: white;
* }
* .my-form.ng-invalid {
* background: red;
* color:white;
* }
* </pre>
*
* @example
<example name="ng-form" deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
<file name="index.html">
<script>
angular.module('formExample', [])
.controller('FormController', ['$scope', function($scope) {
$scope.userType = 'guest';
}]);
</script>
<style>
.my-form {
transition:all linear 0.5s;
background: transparent;
}
.my-form.ng-invalid {
background: red;
}
</style>
<form name="myForm" ng-controller="FormController" class="my-form">
userType: <input name="input" ng-model="userType" required>
<span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
<code>userType = {{userType}}</code><br>
<code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
<code>myForm.input.$error = {{myForm.input.$error}}</code><br>
<code>myForm.$valid = {{myForm.$valid}}</code><br>
<code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
</form>
</file>
<file name="protractor.js" type="protractor">
it('should initialize to model', function() {
var userType = element(by.binding('userType'));
var valid = element(by.binding('myForm.input.$valid'));
expect(userType.getText()).toContain('guest');
expect(valid.getText()).toContain('true');
});
it('should be invalid if empty', function() {
var userType = element(by.binding('userType'));
var valid = element(by.binding('myForm.input.$valid'));
var userInput = element(by.model('userType'));
userInput.clear();
userInput.sendKeys('');
expect(userType.getText()).toEqual('userType =');
expect(valid.getText()).toContain('false');
});
</file>
</example>
*
* @param {string=} name Name of the form. If specified, the form controller will be published into
* related scope, under this name.
*/
var formDirectiveFactory = function(isNgForm) {
return ['$timeout', '$parse', function($timeout, $parse) {
var formDirective = {
name: 'form',
restrict: isNgForm ? 'EAC' : 'E',
require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
controller: FormController,
compile: function ngFormCompile(formElement, attr) {
// Setup initial state of the control
formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
return {
pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
var controller = ctrls[0];
// if `action` attr is not present on the form, prevent the default action (submission)
if (!('action' in attr)) {
// we can't use jq events because if a form is destroyed during submission the default
// action is not prevented. see #1238
//
// IE 9 is not affected because it doesn't fire a submit event and try to do a full
// page reload if the form was destroyed by submission of the form via a click handler
// on a button in the form. Looks like an IE9 specific bug.
var handleFormSubmission = function(event) {
scope.$apply(function() {
controller.$commitViewValue();
controller.$setSubmitted();
});
event.preventDefault();
};
formElement[0].addEventListener('submit', handleFormSubmission);
// unregister the preventDefault listener so that we don't not leak memory but in a
// way that will achieve the prevention of the default action.
formElement.on('$destroy', function() {
$timeout(function() {
formElement[0].removeEventListener('submit', handleFormSubmission);
}, 0, false);
});
}
var parentFormCtrl = ctrls[1] || controller.$$parentForm;
parentFormCtrl.$addControl(controller);
var setter = nameAttr ? getSetter(controller.$name) : noop;
if (nameAttr) {
setter(scope, controller);
attr.$observe(nameAttr, function(newValue) {
if (controller.$name === newValue) return;
setter(scope, undefined);
controller.$$parentForm.$$renameControl(controller, newValue);
setter = getSetter(controller.$name);
setter(scope, controller);
});
}
formElement.on('$destroy', function() {
controller.$$parentForm.$removeControl(controller);
setter(scope, undefined);
extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
});
}
};
}
};
return formDirective;
function getSetter(expression) {
if (expression === '') {
//create an assignable expression, so forms with an empty name can be renamed later
return $parse('this[""]').assign;
}
return $parse(expression).assign || noop;
}
}];
};
var formDirective = formDirectiveFactory();
var ngFormDirective = formDirectiveFactory(true);
// helper methods
function setupValidity(instance) {
instance.$$classCache = {};
instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] = instance.$$element.hasClass(VALID_CLASS));
}
function addSetValidityMethod(context) {
var clazz = context.clazz,
set = context.set,
unset = context.unset;
clazz.prototype.$setValidity = function(validationErrorKey, state, controller) {
if (isUndefined(state)) {
createAndSet(this, '$pending', validationErrorKey, controller);
} else {
unsetAndCleanup(this, '$pending', validationErrorKey, controller);
}
if (!isBoolean(state)) {
unset(this.$error, validationErrorKey, controller);
unset(this.$$success, validationErrorKey, controller);
} else {
if (state) {
unset(this.$error, validationErrorKey, controller);
set(this.$$success, validationErrorKey, controller);
} else {
set(this.$error, validationErrorKey, controller);
unset(this.$$success, validationErrorKey, controller);
}
}
if (this.$pending) {
cachedToggleClass(this, PENDING_CLASS, true);
this.$valid = this.$invalid = undefined;
toggleValidationCss(this, '', null);
} else {
cachedToggleClass(this, PENDING_CLASS, false);
this.$valid = isObjectEmpty(this.$error);
this.$invalid = !this.$valid;
toggleValidationCss(this, '', this.$valid);
}
// re-read the state as the set/unset methods could have
// combined state in this.$error[validationError] (used for forms),
// where setting/unsetting only increments/decrements the value,
// and does not replace it.
var combinedState;
if (this.$pending && this.$pending[validationErrorKey]) {
combinedState = undefined;
} else if (this.$error[validationErrorKey]) {
combinedState = false;
} else if (this.$$success[validationErrorKey]) {
combinedState = true;
} else {
combinedState = null;
}
toggleValidationCss(this, validationErrorKey, combinedState);
this.$$parentForm.$setValidity(validationErrorKey, combinedState, this);
};
function createAndSet(ctrl, name, value, controller) {
if (!ctrl[name]) {
ctrl[name] = {};
}
set(ctrl[name], value, controller);
}
function unsetAndCleanup(ctrl, name, value, controller) {
if (ctrl[name]) {
unset(ctrl[name], value, controller);
}
if (isObjectEmpty(ctrl[name])) {
ctrl[name] = undefined;
}
}
function cachedToggleClass(ctrl, className, switchValue) {
if (switchValue && !ctrl.$$classCache[className]) {
ctrl.$$animate.addClass(ctrl.$$element, className);
ctrl.$$classCache[className] = true;
} else if (!switchValue && ctrl.$$classCache[className]) {
ctrl.$$animate.removeClass(ctrl.$$element, className);
ctrl.$$classCache[className] = false;
}
}
function toggleValidationCss(ctrl, validationErrorKey, isValid) {
validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true);
cachedToggleClass(ctrl, INVALID_CLASS + validationErrorKey, isValid === false);
}
}
function isObjectEmpty(obj) {
if (obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
return false;
}
}
}
return true;
}