Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 733a97a

Browse files
pkozlowski-opensourceIgorMinar
authored andcommitted
feat(form): add ability to reset a form to pristine state
Retting a form to pristine state will cause all of the nested form and form controls to be recursively reset as well. Closes #856
1 parent 96ed9ff commit 733a97a

File tree

4 files changed

+165
-2
lines changed

4 files changed

+165
-2
lines changed

src/ng/directive/form.js

+31-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ var nullFormCtrl = {
55
$addControl: noop,
66
$removeControl: noop,
77
$setValidity: noop,
8-
$setDirty: noop
8+
$setDirty: noop,
9+
$setPristine: noop
910
};
1011

1112
/**
@@ -37,7 +38,8 @@ function FormController(element, attrs) {
3738
var form = this,
3839
parentForm = element.parent().controller('form') || nullFormCtrl,
3940
invalidCount = 0, // used to easily determine if we are valid
40-
errors = form.$error = {};
41+
errors = form.$error = {},
42+
controls = [];
4143

4244
// init state
4345
form.$name = attrs.name;
@@ -61,6 +63,8 @@ function FormController(element, attrs) {
6163
}
6264

6365
form.$addControl = function(control) {
66+
controls.push(control);
67+
6468
if (control.$name && !form.hasOwnProperty(control.$name)) {
6569
form[control.$name] = control;
6670
}
@@ -73,6 +77,8 @@ function FormController(element, attrs) {
7377
forEach(errors, function(queue, validationToken) {
7478
form.$setValidity(validationToken, true, control);
7579
});
80+
81+
arrayRemove(controls, control);
7682
};
7783

7884
form.$setValidity = function(validationToken, isValid, control) {
@@ -120,6 +126,29 @@ function FormController(element, attrs) {
120126
parentForm.$setDirty();
121127
};
122128

129+
/**
130+
* @ngdoc function
131+
* @name ng.directive:form.FormController#$setPristine
132+
* @methodOf ng.directive:form.FormController
133+
*
134+
* @description
135+
* Sets the form to its pristine state.
136+
*
137+
* This method can be called to remove the 'ng-dirty' class and set the form to its pristine
138+
* state (ng-pristine class). This method will also propagate to all the controls contained
139+
* in this form.
140+
*
141+
* Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
142+
* saving or resetting it.
143+
*/
144+
form.$setPristine = function () {
145+
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
146+
form.$dirty = false;
147+
form.$pristine = true;
148+
forEach(controls, function(control) {
149+
control.$setPristine();
150+
});
151+
};
123152
}
124153

125154

src/ng/directive/input.js

+16
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
978978
parentForm.$setValidity(validationErrorKey, isValid, this);
979979
};
980980

981+
/**
982+
* @ngdoc function
983+
* @name ng.directive:ngModel.NgModelController#$setPristine
984+
* @methodOf ng.directive:ngModel.NgModelController
985+
*
986+
* @description
987+
* Sets the control to its pristine state.
988+
*
989+
* This method can be called to remove the 'ng-dirty' class and set the control to its pristine
990+
* state (ng-pristine class).
991+
*/
992+
this.$setPristine = function () {
993+
this.$dirty = false;
994+
this.$pristine = true;
995+
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
996+
};
981997

982998
/**
983999
* @ngdoc function

test/ng/directive/formSpec.js

+106
Original file line numberDiff line numberDiff line change
@@ -430,4 +430,110 @@ describe('form', function() {
430430
expect(doc).toBeDirty();
431431
});
432432
});
433+
434+
435+
describe('$setPristine', function() {
436+
437+
it('should reset pristine state of form and controls', function() {
438+
439+
doc = $compile(
440+
'<form name="testForm">' +
441+
'<input ng-model="named1" name="foo">' +
442+
'<input ng-model="named2" name="bar">' +
443+
'</form>')(scope);
444+
445+
scope.$digest();
446+
447+
var form = doc,
448+
formCtrl = scope.testForm,
449+
input1 = form.find('input').eq(0),
450+
input1Ctrl = input1.controller('ngModel'),
451+
input2 = form.find('input').eq(1),
452+
input2Ctrl = input2.controller('ngModel');
453+
454+
input1Ctrl.$setViewValue('xx');
455+
input2Ctrl.$setViewValue('yy');
456+
scope.$apply();
457+
expect(form).toBeDirty();
458+
expect(input1).toBeDirty();
459+
expect(input2).toBeDirty();
460+
461+
formCtrl.$setPristine();
462+
expect(form).toBePristine();
463+
expect(formCtrl.$pristine).toBe(true);
464+
expect(formCtrl.$dirty).toBe(false);
465+
expect(input1).toBePristine();
466+
expect(input1Ctrl.$pristine).toBe(true);
467+
expect(input1Ctrl.$dirty).toBe(false);
468+
expect(input2).toBePristine();
469+
expect(input2Ctrl.$pristine).toBe(true);
470+
expect(input2Ctrl.$dirty).toBe(false);
471+
});
472+
473+
474+
it('should reset pristine state of anonymous form controls', function() {
475+
476+
doc = $compile(
477+
'<form name="testForm">' +
478+
'<input ng-model="anonymous">' +
479+
'</form>')(scope);
480+
481+
scope.$digest();
482+
483+
var form = doc,
484+
formCtrl = scope.testForm,
485+
input = form.find('input').eq(0),
486+
inputCtrl = input.controller('ngModel');
487+
488+
inputCtrl.$setViewValue('xx');
489+
scope.$apply();
490+
expect(form).toBeDirty();
491+
expect(input).toBeDirty();
492+
493+
formCtrl.$setPristine();
494+
expect(form).toBePristine();
495+
expect(formCtrl.$pristine).toBe(true);
496+
expect(formCtrl.$dirty).toBe(false);
497+
expect(input).toBePristine();
498+
expect(inputCtrl.$pristine).toBe(true);
499+
expect(inputCtrl.$dirty).toBe(false);
500+
});
501+
502+
503+
it('should reset pristine state of nested forms', function() {
504+
505+
doc = $compile(
506+
'<form name="testForm">' +
507+
'<div ng-form>' +
508+
'<input ng-model="named" name="foo">' +
509+
'</div>' +
510+
'</form>')(scope);
511+
512+
scope.$digest();
513+
514+
var form = doc,
515+
formCtrl = scope.testForm,
516+
nestedForm = form.find('div'),
517+
nestedFormCtrl = nestedForm.controller('form'),
518+
nestedInput = form.find('input').eq(0),
519+
nestedInputCtrl = nestedInput.controller('ngModel');
520+
521+
nestedInputCtrl.$setViewValue('xx');
522+
scope.$apply();
523+
expect(form).toBeDirty();
524+
expect(nestedForm).toBeDirty();
525+
expect(nestedInput).toBeDirty();
526+
527+
formCtrl.$setPristine();
528+
expect(form).toBePristine();
529+
expect(formCtrl.$pristine).toBe(true);
530+
expect(formCtrl.$dirty).toBe(false);
531+
expect(nestedForm).toBePristine();
532+
expect(nestedFormCtrl.$pristine).toBe(true);
533+
expect(nestedFormCtrl.$dirty).toBe(false);
534+
expect(nestedInput).toBePristine();
535+
expect(nestedInputCtrl.$pristine).toBe(true);
536+
expect(nestedInputCtrl.$dirty).toBe(false);
537+
});
538+
});
433539
});

test/ng/directive/inputSpec.js

+12
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ describe('NgModelController', function() {
117117
});
118118
});
119119

120+
describe('setPristine', function() {
121+
122+
it('should set control to its pristine state', function() {
123+
ctrl.$setViewValue('edit');
124+
expect(ctrl.$dirty).toBe(true);
125+
expect(ctrl.$pristine).toBe(false);
126+
127+
ctrl.$setPristine();
128+
expect(ctrl.$dirty).toBe(false);
129+
expect(ctrl.$pristine).toBe(true);
130+
});
131+
});
120132

121133
describe('view -> model', function() {
122134

0 commit comments

Comments
 (0)