'use strict'; var nullFormCtrl = { $addControl: noop, $removeControl: noop, $setValidity: noop, $setDirty: noop } /** * @ngdoc object * @name angular.module.ng.$compileProvider.directive.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 containg forms and controls are valid. * @property {boolean} $invalid True if at least one containing control or form is invalid. * * @property {Object} $error Is an object hash, containing references to all invalid controls or * forms, where: * * - keys are validation tokens (error names) — such as `REQUIRED`, `URL` or `EMAIL`), * - values are arrays of controls or forms that are invalid with given error. * * @description * `FormController` keeps track of all its controls and nested forms as well as state of them, * such as being valid/invalid or dirty/pristine. * * Each {@link angular.module.ng.$compileProvider.directive.form form} directive creates an instance * of `FormController`. * */ FormController.$inject = ['name', '$element', '$attrs']; function FormController(name, element, attrs) { var form = this, parentForm = element.parent().inheritedData('$formController') || nullFormCtrl, invalidCount = 0, // used to easily determine if we are valid errors = form.$error = {}; // init state form.$name = attrs.name; form.$dirty = false; form.$pristine = true; form.$valid = true; form.$invalid = false; // publish the form into scope name(this); parentForm.$addControl(form); // Setup initial state of the control element.addClass(PRISTINE_CLASS); toggleValidCss(true); // convenience method for easy toggling of classes function toggleValidCss(isValid, validationErrorKey) { validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; element. removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); } if (parentForm) { parentForm.$addControl(form); } form.$addControl = function(control) { if (control.$name && !form.hasOwnProperty(control.$name)) { form[control.$name] = control; } }; form.$removeControl = function(control) { if (control.$name && form[control.$name] === control) { delete form[control.$name]; } forEach(errors, cleanupControlErrors, control); }; form.$setValidity = function(validationToken, isValid, control) { if (isValid) { cleanupControlErrors(errors[validationToken], validationToken, control); if (!invalidCount) { toggleValidCss(isValid); form.$valid = true; form.$invalid = false; } } else { if (!invalidCount) { toggleValidCss(isValid); } addControlError(validationToken, control); form.$valid = false; form.$invalid = true; } }; form.$setDirty = function() { element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); form.$dirty = true; form.$pristine = false; }; function cleanupControlErrors(queue, validationToken, control) { if (queue) { control = control || this; // so that we can be used in forEach; arrayRemove(queue, control); if (!queue.length) { invalidCount--; errors[validationToken] = false; toggleValidCss(true, validationToken); parentForm.$setValidity(validationToken, true, form); } } } function addControlError(validationToken, control) { var queue = errors[validationToken]; if (queue) { if (includes(queue, control)) return; } else { errors[validationToken] = queue = []; invalidCount++; toggleValidCss(false, validationToken); parentForm.$setValidity(validationToken, false, form); } queue.push(control); } } /** * @ngdoc directive * @name angular.module.ng.$compileProvider.directive.form * @restrict EA * * @description * Directive that instantiates * {@link angular.module.ng.$compileProvider.directive.form.FormController FormController}. * * If `name` attribute is specified, the form controller is published onto the current scope under * this name. * * # Alias: `ng-form` * * In angular 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 `