| Index: third_party/pkg/angular/lib/directive/ng_control.dart
|
| diff --git a/third_party/pkg/angular/lib/directive/ng_control.dart b/third_party/pkg/angular/lib/directive/ng_control.dart
|
| index 3c6f9690c4ab112743a360bd0821dd64dce00dfd..c986b64a41f482dd483d2468b0a4b91973db82fb 100644
|
| --- a/third_party/pkg/angular/lib/directive/ng_control.dart
|
| +++ b/third_party/pkg/angular/lib/directive/ng_control.dart
|
| @@ -1,138 +1,146 @@
|
| part of angular.directive;
|
|
|
| -abstract class NgControl implements NgDetachAware {
|
| - static const NG_VALID_CLASS = "ng-valid";
|
| - static const NG_INVALID_CLASS = "ng-invalid";
|
| - static const NG_PRISTINE_CLASS = "ng-pristine";
|
| - static const NG_DIRTY_CLASS = "ng-dirty";
|
| - static const NG_TOUCHED_CLASS = "ng-touched";
|
| - static const NG_UNTOUCHED_CLASS = "ng-untouched";
|
| - static const NG_SUBMIT_VALID_CLASS = "ng-submit-valid";
|
| - static const NG_SUBMIT_INVALID_CLASS = "ng-submit-invalid";
|
| +/**
|
| + * The NgControl class is a super-class for handling info and error states between
|
| + * inner controls and models. NgControl will automatically apply the associated CSS
|
| + * classes for the error and info states that are applied as well as status flags.
|
| + * NgControl is used with the form and fieldset as well as all other directives that
|
| + * are used for user input with NgModel.
|
| + */
|
| +abstract class NgControl implements AttachAware, DetachAware {
|
| + static const NG_VALID = "ng-valid";
|
| + static const NG_INVALID = "ng-invalid";
|
| + static const NG_PRISTINE = "ng-pristine";
|
| + static const NG_DIRTY = "ng-dirty";
|
| + static const NG_TOUCHED = "ng-touched";
|
| + static const NG_UNTOUCHED = "ng-untouched";
|
| + static const NG_SUBMIT_VALID = "ng-submit-valid";
|
| + static const NG_SUBMIT_INVALID = "ng-submit-invalid";
|
|
|
| String _name;
|
| - bool _dirty;
|
| - bool _pristine;
|
| - bool _valid;
|
| - bool _invalid;
|
| - bool _touched;
|
| - bool _untouched;
|
| - bool _submit_valid;
|
| -
|
| - final Scope _scope;
|
| + bool _submitValid;
|
| +
|
| final NgControl _parentControl;
|
| - dom.Element _element;
|
| + final Animate _animate;
|
| + final NgElement _element;
|
| +
|
| + final _controls = new List<NgControl>();
|
| + final _controlByName = new Map<String, List<NgControl>>();
|
|
|
| - final Map<String, List<NgControl>> errors = new Map<String, List<NgControl>>();
|
| - final List<NgControl> _controls = new List<NgControl>();
|
| - final Map<String, NgControl> _controlByName = new Map<String, NgControl>();
|
| + /**
|
| + * The list of errors present on the control represented by an error name and
|
| + * an inner control instance.
|
| + */
|
| + final errorStates = new Map<String, Set<NgControl>>();
|
| +
|
| + /**
|
| + * The list of info messages present on the control represented by an state name and
|
| + * an inner control instance.
|
| + */
|
| + final infoStates = new Map<String, Set<NgControl>>();
|
|
|
| - NgControl(Scope this._scope, dom.Element this._element, Injector injector)
|
| - : _parentControl = injector.parent.get(NgControl)
|
| - {
|
| - pristine = true;
|
| - untouched = true;
|
| + NgControl(NgElement this._element, Injector injector,
|
| + Animate this._animate)
|
| + : _parentControl = injector.parent.get(NgControl);
|
|
|
| - _scope.on('submitNgControl').listen((e) => _onSubmit(e.data));
|
| + @override
|
| + void attach() {
|
| + _parentControl.addControl(this);
|
| }
|
|
|
| - detach() {
|
| - for (int i = _controls.length - 1; i >= 0; --i) {
|
| - removeControl(_controls[i]);
|
| - }
|
| + @override
|
| + void detach() {
|
| + _parentControl..removeStates(this)..removeControl(this);
|
| }
|
|
|
| - reset() {
|
| - _scope.broadcast('resetNgModel');
|
| - untouched = true;
|
| + /**
|
| + * Resets the form and inner models to their pristine state.
|
| + */
|
| + void reset() {
|
| + _controls.forEach((control) {
|
| + control.reset();
|
| + });
|
| }
|
|
|
| - _onSubmit(bool valid) {
|
| + void onSubmit(bool valid) {
|
| if (valid) {
|
| - _submit_valid = true;
|
| - element.classes..add(NG_SUBMIT_VALID_CLASS)..remove(NG_SUBMIT_INVALID_CLASS);
|
| + _submitValid = true;
|
| + element..addClass(NG_SUBMIT_VALID)..removeClass(NG_SUBMIT_INVALID);
|
| } else {
|
| - _submit_valid = false;
|
| - element.classes..add(NG_SUBMIT_INVALID_CLASS)..remove(NG_SUBMIT_VALID_CLASS);
|
| + _submitValid = false;
|
| + element..addClass(NG_SUBMIT_INVALID)..removeClass(NG_SUBMIT_VALID);
|
| }
|
| + _controls.forEach((control) {
|
| + control.onSubmit(valid);
|
| + });
|
| }
|
|
|
| - get submitted => _submit_valid != null;
|
| - get valid_submit => _submit_valid == true;
|
| - get invalid_submit => _submit_valid == false;
|
| + NgControl get parentControl => _parentControl;
|
|
|
| - get name => _name;
|
| - set name(value) {
|
| - _name = value;
|
| - _parentControl.addControl(this);
|
| - }
|
| -
|
| - get element => _element;
|
| -
|
| - get pristine => _pristine;
|
| - set pristine(value) {
|
| - _pristine = true;
|
| - _dirty = false;
|
| -
|
| - element.classes..remove(NG_DIRTY_CLASS)..add(NG_PRISTINE_CLASS);
|
| - }
|
| + /**
|
| + * Whether or not the form has been submitted yet.
|
| + */
|
| + bool get submitted => _submitValid != null;
|
|
|
| - get dirty => _dirty;
|
| - set dirty(value) {
|
| - _dirty = true;
|
| - _pristine = false;
|
| + /**
|
| + * Whether or not the form was valid when last submitted.
|
| + */
|
| + bool get validSubmit => _submitValid == true;
|
|
|
| - element.classes..remove(NG_PRISTINE_CLASS)..add(NG_DIRTY_CLASS);
|
| + /**
|
| + * Whether or not the form was invalid when last submitted.
|
| + */
|
| + bool get invalidSubmit => _submitValid == false;
|
|
|
| - //as soon as one of the controls/models is modified
|
| - //then all of the parent controls are dirty as well
|
| - _parentControl.dirty = true;
|
| + String get name => _name;
|
| + void set name(value) {
|
| + _name = value;
|
| }
|
|
|
| - get valid => _valid;
|
| - set valid(value) {
|
| - _invalid = false;
|
| - _valid = true;
|
| -
|
| - element.classes..remove(NG_INVALID_CLASS)..add(NG_VALID_CLASS);
|
| - }
|
| + /**
|
| + * Whether or not the form was invalid when last submitted.
|
| + */
|
| + NgElement get element => _element;
|
|
|
| - get invalid => _invalid;
|
| - set invalid(value) {
|
| - _valid = false;
|
| - _invalid = true;
|
| + /**
|
| + * A control is considered valid if all inner models are valid.
|
| + */
|
| + bool get valid => !invalid;
|
|
|
| - element.classes..remove(NG_VALID_CLASS)..add(NG_INVALID_CLASS);
|
| - }
|
| + /**
|
| + * A control is considered invalid if any inner models are invalid.
|
| + */
|
| + bool get invalid => errorStates.isNotEmpty;
|
|
|
| - get touched => _touched;
|
| - set touched(value) {
|
| - _touched = true;
|
| - _untouched = false;
|
| + /**
|
| + * Whether or not the control's or model's data has not been changed.
|
| + */
|
| + bool get pristine => !dirty;
|
|
|
| - element.classes..remove(NG_UNTOUCHED_CLASS)..add(NG_TOUCHED_CLASS);
|
| + /**
|
| + * Whether or not the control's or model's data has been changed.
|
| + */
|
| + bool get dirty => infoStates.containsKey(NG_DIRTY);
|
|
|
| - //as soon as one of the controls/models is touched
|
| - //then all of the parent controls are touched as well
|
| - _parentControl.touched = true;
|
| - }
|
| + /**
|
| + * Whether or not the control/model has not been interacted with by the user.
|
| + */
|
| + bool get untouched => !touched;
|
|
|
| - get untouched => _untouched;
|
| - set untouched(value) {
|
| - _touched = false;
|
| - _untouched = true;
|
| - element.classes..remove(NG_TOUCHED_CLASS)..add(NG_UNTOUCHED_CLASS);
|
| - }
|
| + /**
|
| + * Whether or not the control/model has been interacted with by the user.
|
| + */
|
| + bool get touched => infoStates.containsKey(NG_TOUCHED);
|
|
|
| /**
|
| * Registers a form control into the form for validation.
|
| *
|
| - * * [control] - The form control which will be registered (see [ngControl]).
|
| + * * [control] - The form control which will be registered (see [NgControl]).
|
| */
|
| - addControl(NgControl control) {
|
| + void addControl(NgControl control) {
|
| _controls.add(control);
|
| if (control.name != null) {
|
| - _controlByName[control.name] = control;
|
| + _controlByName.putIfAbsent(control.name, () => <NgControl>[]).add(control);
|
| }
|
| }
|
|
|
| @@ -140,96 +148,180 @@ abstract class NgControl implements NgDetachAware {
|
| * De-registers a form control from the list of controls associated with the
|
| * form.
|
| *
|
| - * * [control] - The form control which will be de-registered (see
|
| - * [ngControl]).
|
| + * * [control] - The form control which will be de-registered (see [NgControl]).
|
| */
|
| - removeControl(NgControl control) {
|
| + void removeControl(NgControl control) {
|
| _controls.remove(control);
|
| - if (control.name != null) {
|
| - _controlByName.remove(control.name);
|
| + String key = control.name;
|
| + if (key != null && _controlByName.containsKey(key)) {
|
| + _controlByName[key].remove(control);
|
| + if (_controlByName[key].isEmpty) _controlByName.remove(key);
|
| }
|
| }
|
|
|
| /**
|
| - * Sets the validity status of the given control/errorType pair within
|
| - * the list of controls registered on the form. Depending on the validation
|
| - * state of the existing controls, this will either change valid to true
|
| - * or invalid to true depending on if all controls are valid or if one
|
| - * or more of them is invalid.
|
| + * Clears all the info and error states that are associated with the control.
|
| *
|
| - * * [control] - The registered control object (see [ngControl]).
|
| - * * [errorType] - The error associated with the control (e.g. required, url,
|
| - * number, etc...).
|
| - * * [isValid] - Whether the given error is valid or not (false would mean the
|
| - * error is real).
|
| + * * [control] - The form control which will be cleared of all state (see [NgControl]).
|
| */
|
| - updateControlValidity(NgControl control, String errorType, bool isValid) {
|
| - List queue = errors[errorType];
|
| -
|
| - if (isValid) {
|
| - if (queue != null) {
|
| - queue.remove(control);
|
| - if (queue.isEmpty) {
|
| - errors.remove(errorType);
|
| - _parentControl.updateControlValidity(this, errorType, true);
|
| - }
|
| + void removeStates(NgControl control) {
|
| + bool hasRemovals = false;
|
| + errorStates.keys.toList().forEach((state) {
|
| + Set matchingControls = errorStates[state];
|
| + matchingControls.remove(control);
|
| + if (matchingControls.isEmpty) {
|
| + errorStates.remove(state);
|
| + hasRemovals = true;
|
| }
|
| - if (errors.isEmpty) {
|
| - valid = true;
|
| + });
|
| +
|
| + infoStates.keys.toList().forEach((state) {
|
| + Set matchingControls = infoStates[state];
|
| + matchingControls.remove(control);
|
| + if (matchingControls.isEmpty) {
|
| + infoStates.remove(state);
|
| + hasRemovals = true;
|
| }
|
| - } else {
|
| - if (queue == null) {
|
| - queue = new List<NgControl>();
|
| - errors[errorType] = queue;
|
| - _parentControl.updateControlValidity(this, errorType, false);
|
| - } else if (queue.contains(control)) return;
|
| -
|
| - queue.add(control);
|
| - invalid = true;
|
| + });
|
| +
|
| + if (hasRemovals) _parentControl.removeStates(this);
|
| + }
|
| +
|
| + /**
|
| + * Whether or not the control contains the given error.
|
| + *
|
| + * * [errorName] - The name of the error (e.g. ng-required, ng-pattern, etc...)
|
| + */
|
| + bool hasErrorState(String errorName) => errorStates.containsKey(errorName);
|
| +
|
| + /**
|
| + * Adds the given childControl/errorName to the list of errors present on the control. Once
|
| + * added all associated parent controls will be registered with the error as well.
|
| + *
|
| + * * [childControl] - The child control that contains the error.
|
| + * * [errorName] - The name of the given error (e.g. ng-required, ng-pattern, etc...).
|
| + */
|
| + void addErrorState(NgControl childControl, String errorName) {
|
| + element..addClass(errorName + '-invalid')..removeClass(errorName + '-valid');
|
| + errorStates.putIfAbsent(errorName, () => new Set()).add(childControl);
|
| + _parentControl.addErrorState(this, errorName);
|
| + }
|
| +
|
| + /**
|
| + * Removes the given childControl/errorName from the list of errors present on the control. Once
|
| + * removed the control will update any parent controls depending if error is not present on
|
| + * any other inner controls and or models.
|
| + *
|
| + * * [childControl] - The child control that contains the error.
|
| + * * [errorName] - The name of the given error (e.g. ng-required, ng-pattern, etc...).
|
| + */
|
| + void removeErrorState(NgControl childControl, String errorName) {
|
| + if (!errorStates.containsKey(errorName)) return;
|
| +
|
| + bool hasError = _controls.any((child) => child.hasErrorState(errorName));
|
| + if (!hasError) {
|
| + errorStates.remove(errorName);
|
| + _parentControl.removeErrorState(this, errorName);
|
| + element..removeClass(errorName + '-invalid')..addClass(errorName + '-valid');
|
| }
|
| }
|
| -}
|
|
|
| -class NgNullControl implements NgControl {
|
| - var _name, _dirty, _valid, _invalid, _submit_valid, _pristine, _element;
|
| - var _touched, _untouched;
|
| - var _controls, _scope, _parentControl, _controlName;
|
| - var errors, _controlByName;
|
| - dom.Element element;
|
| + String _getOppositeInfoState(String state) {
|
| + switch(state) {
|
| + case NG_DIRTY:
|
| + return NG_PRISTINE;
|
| + case NG_TOUCHED:
|
| + return NG_UNTOUCHED;
|
| + default:
|
| + //not all info states have an opposite value
|
| + return null;
|
| + }
|
| + }
|
|
|
| - NgNullControl() {}
|
| - _onSubmit(bool valid) {}
|
| + /**
|
| + * Registers a non-error state on the control with the given childControl/stateName data. Once
|
| + * added the control will also add the same data to any associated parent controls.
|
| + *
|
| + * * [childControl] - The child control that contains the error.
|
| + * * [stateName] - The name of the given error (e.g. ng-required, ng-pattern, etc...).
|
| + */
|
| + void addInfoState(NgControl childControl, String stateName) {
|
| + String oppositeState = _getOppositeInfoState(stateName);
|
| + if (oppositeState != null) element.removeClass(oppositeState);
|
| + element.addClass(stateName);
|
| + infoStates.putIfAbsent(stateName, () => new Set()).add(childControl);
|
| + _parentControl.addInfoState(this, stateName);
|
| + }
|
|
|
| - addControl(control) {}
|
| - removeControl(control) {}
|
| - updateControlValidity(NgControl control, String errorType, bool isValid) {}
|
| + /**
|
| + * De-registers the provided state on the control with the given childControl. The state
|
| + * will be fully removed from the control if all of the inner controls/models also do not
|
| + * contain the state. If so then the state will also be attempted to be removed from the
|
| + * associated parent controls.
|
| + *
|
| + * * [childControl] - The child control that contains the error.
|
| + * * [stateName] - The name of the given error (e.g. ng-required, ng-pattern, etc...).
|
| + */
|
| + void removeInfoState(NgControl childControl, String stateName) {
|
| + String oppositeState = _getOppositeInfoState(stateName);
|
| + if (infoStates.containsKey(stateName)) {
|
| + bool hasState = _controls.any((child) =>
|
| + child.infoStates.containsKey(stateName));
|
| + if (!hasState) {
|
| + if (oppositeState != null) element.addClass(oppositeState);
|
| + element.removeClass(stateName);
|
| + infoStates.remove(stateName);
|
| + _parentControl.removeInfoState(this, stateName);
|
| + }
|
| + } else if (oppositeState != null) {
|
| + NgControl parent = this;
|
| + do {
|
| + parent.element..addClass(oppositeState)..removeClass(stateName);
|
| + parent = parent.parentControl;
|
| + }
|
| + while(parent != null && parent is! NgNullControl);
|
| + }
|
| + }
|
| +}
|
|
|
| - get name => null;
|
| - set name(name) {}
|
| +class NgNullControl implements NgControl {
|
| + var _name, _dirty, _valid, _submitValid, _pristine, _element, _touched;
|
| + var _controls, _parentControl, _controlName, _animate, infoStates, errorStates;
|
| + var errors, _controlByName;
|
| + NgElement element;
|
|
|
| - get submitted => null;
|
| - get valid_submit => null;
|
| - get invalid_submit => null;
|
| + void onSubmit(bool valid) {}
|
|
|
| - get pristine => null;
|
| - set pristine(value) {}
|
| + void addControl(control) {}
|
| + void removeControl(control) {}
|
| + void updateControlValidity(NgControl ctrl, String errorType, bool isValid) {}
|
|
|
| - get dirty => null;
|
| - set dirty(value) {}
|
| + String get name => null;
|
| + void set name(name) {}
|
|
|
| - get valid => null;
|
| - set valid(value) {}
|
| + bool get submitted => false;
|
| + bool get validSubmit => true;
|
| + bool get invalidSubmit => false;
|
| + bool get pristine => true;
|
| + bool get dirty => false;
|
| + bool get valid => true;
|
| + bool get invalid => false;
|
| + bool get touched => false;
|
| + bool get untouched => true;
|
|
|
| - get invalid => null;
|
| - set invalid(value) {}
|
| + get parentControl => null;
|
|
|
| - get touched => null;
|
| - set touched(value) {}
|
| + String _getOppositeInfoState(String state) => null;
|
| + void addErrorState(NgControl control, String state) {}
|
| + void removeErrorState(NgControl control, String state) {}
|
| + void addInfoState(NgControl control, String state) {}
|
| + void removeInfoState(NgControl control, String state) {}
|
|
|
| - get untouched => null;
|
| - set untouched(value) {}
|
| + void reset() {}
|
| + void attach() {}
|
| + void detach() {}
|
|
|
| - reset() => null;
|
| - detach() => null;
|
| + bool hasErrorState(String key) => false;
|
|
|
| + void removeStates(NgControl control) {}
|
| }
|
|
|