Skip to content

Commit

Permalink
feat(form): add $dirtyAfter state to form controls
Browse files Browse the repository at this point in the history
add $dirtyAfter state to form controls to help with inline form validation

contribute to issue angular#583
  • Loading branch information
troch committed Jan 19, 2014
1 parent 6c9131e commit 7c6de9a
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 2 deletions.
25 changes: 23 additions & 2 deletions src/ng/directive/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ var nullFormCtrl = {
$removeControl: noop,
$setValidity: noop,
$setDirty: noop,
$setPristine: noop
$setPristine: noop,
$updateControlsDirtyAfterState: noop
};

/**
Expand Down Expand Up @@ -180,6 +181,7 @@ function FormController(element, attrs) {
element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
form.$dirty = true;
form.$pristine = false;
form.$updateControlsDirtyAfterState();
parentForm.$setDirty();
};

Expand All @@ -205,9 +207,28 @@ function FormController(element, attrs) {
forEach(controls, function(control) {
control.$setPristine();
});
form.$updateControlsDirtyAfterState();
};
}

/**
* @ngdoc function
* @name ng.directive:form.FormController#$updateControlsDirtyAfterState
* @methodOf ng.directive:form.FormController
*
* @description
* Update the form controls dirtyAfter state.
*
* This method is called when an input dirty state changes
* or when the form is set to its pristine state.
*/
form.$updateControlsDirtyAfterState = function() {
var dirtyAfter = false;
for (var i = controls.length - 1; i >= 0; i--) {
controls[i].$dirtyAfter = dirtyAfter;
dirtyAfter = dirtyAfter || controls[i].$dirty;
}
};
}

/**
* @ngdoc directive
Expand Down
2 changes: 2 additions & 0 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,7 @@ var VALID_CLASS = 'ng-valid',
*
* @property {boolean} $pristine True if user has not interacted with the control yet.
* @property {boolean} $dirty True if user has already interacted with the control.
* @property {boolean} $dirtyAfter True if user has already interacted with any control positioned after this control.
* @property {boolean} $valid True if there is no error.
* @property {boolean} $invalid True if at least one error on the control.
*
Expand Down Expand Up @@ -931,6 +932,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
this.$viewChangeListeners = [];
this.$pristine = true;
this.$dirty = false;
this.$dirtyAfter = false;
this.$valid = true;
this.$invalid = false;
this.$name = $attr.name;
Expand Down
47 changes: 47 additions & 0 deletions test/ng/directive/formSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,4 +593,51 @@ describe('form', function() {
expect(nestedInputCtrl.$dirty).toBe(false);
});
});

describe('$updateControlsDirtyAfterState', function() {

it('should update controls dirty state when a control becomes dirty or when the form is set pristine', function() {

doc = $compile(
'<form name="testForm">' +
'<input ng-model="named1" name="name1">' +
'<input ng-model="named2" name="name2">' +
'<input ng-model="named3" name="name3">' +
'</form>')(scope);

scope.$digest();

var form = doc,
formCtrl = scope.testForm,
input1 = form.find('input').eq(0),
input1Ctrl = input1.controller('ngModel'),
input2 = form.find('input').eq(1),
input2Ctrl = input2.controller('ngModel'),
input3 = form.find('input').eq(2),
input3Ctrl = input3.controller('ngModel');

input1Ctrl.$setViewValue('first');
scope.$apply();
expect(input1Ctrl.$dirtyAfter).toBe(false);
expect(input2Ctrl.$dirtyAfter).toBe(false);
expect(input3Ctrl.$dirtyAfter).toBe(false);

input2Ctrl.$setViewValue('second');
scope.$apply();
expect(input1Ctrl.$dirtyAfter).toBe(true);
expect(input2Ctrl.$dirtyAfter).toBe(false);
expect(input3Ctrl.$dirtyAfter).toBe(false);

input3Ctrl.$setViewValue('third');
scope.$apply();
expect(input1Ctrl.$dirtyAfter).toBe(true);
expect(input2Ctrl.$dirtyAfter).toBe(true);
expect(input3Ctrl.$dirtyAfter).toBe(false);

formCtrl.$setPristine();
expect(input1Ctrl.$dirtyAfter).toBe(false);
expect(input2Ctrl.$dirtyAfter).toBe(false);
expect(input3Ctrl.$dirtyAfter).toBe(false);
});
});
});

0 comments on commit 7c6de9a

Please sign in to comment.