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
This commit is contained in:
Pawel Kozlowski 2012-07-07 19:02:04 +02:00 committed by Igor Minar
parent 96ed9ff59a
commit 733a97adf8
4 changed files with 165 additions and 2 deletions

View file

@ -5,7 +5,8 @@ var nullFormCtrl = {
$addControl: noop,
$removeControl: noop,
$setValidity: noop,
$setDirty: noop
$setDirty: noop,
$setPristine: noop
};
/**
@ -37,7 +38,8 @@ function FormController(element, attrs) {
var form = this,
parentForm = element.parent().controller('form') || nullFormCtrl,
invalidCount = 0, // used to easily determine if we are valid
errors = form.$error = {};
errors = form.$error = {},
controls = [];
// init state
form.$name = attrs.name;
@ -61,6 +63,8 @@ function FormController(element, attrs) {
}
form.$addControl = function(control) {
controls.push(control);
if (control.$name && !form.hasOwnProperty(control.$name)) {
form[control.$name] = control;
}
@ -73,6 +77,8 @@ function FormController(element, attrs) {
forEach(errors, function(queue, validationToken) {
form.$setValidity(validationToken, true, control);
});
arrayRemove(controls, control);
};
form.$setValidity = function(validationToken, isValid, control) {
@ -120,6 +126,29 @@ function FormController(element, attrs) {
parentForm.$setDirty();
};
/**
* @ngdoc function
* @name ng.directive:form.FormController#$setPristine
* @methodOf ng.directive:form.FormController
*
* @description
* Sets the form to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the form to its pristine
* state (ng-pristine class). 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.
*/
form.$setPristine = function () {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
forEach(controls, function(control) {
control.$setPristine();
});
};
}

View file

@ -978,6 +978,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
parentForm.$setValidity(validationErrorKey, isValid, this);
};
/**
* @ngdoc function
* @name ng.directive:ngModel.NgModelController#$setPristine
* @methodOf ng.directive:ngModel.NgModelController
*
* @description
* Sets the control to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the control to its pristine
* state (ng-pristine class).
*/
this.$setPristine = function () {
this.$dirty = false;
this.$pristine = true;
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
};
/**
* @ngdoc function

View file

@ -430,4 +430,110 @@ describe('form', function() {
expect(doc).toBeDirty();
});
});
describe('$setPristine', function() {
it('should reset pristine state of form and controls', function() {
doc = $compile(
'<form name="testForm">' +
'<input ng-model="named1" name="foo">' +
'<input ng-model="named2" name="bar">' +
'</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');
input1Ctrl.$setViewValue('xx');
input2Ctrl.$setViewValue('yy');
scope.$apply();
expect(form).toBeDirty();
expect(input1).toBeDirty();
expect(input2).toBeDirty();
formCtrl.$setPristine();
expect(form).toBePristine();
expect(formCtrl.$pristine).toBe(true);
expect(formCtrl.$dirty).toBe(false);
expect(input1).toBePristine();
expect(input1Ctrl.$pristine).toBe(true);
expect(input1Ctrl.$dirty).toBe(false);
expect(input2).toBePristine();
expect(input2Ctrl.$pristine).toBe(true);
expect(input2Ctrl.$dirty).toBe(false);
});
it('should reset pristine state of anonymous form controls', function() {
doc = $compile(
'<form name="testForm">' +
'<input ng-model="anonymous">' +
'</form>')(scope);
scope.$digest();
var form = doc,
formCtrl = scope.testForm,
input = form.find('input').eq(0),
inputCtrl = input.controller('ngModel');
inputCtrl.$setViewValue('xx');
scope.$apply();
expect(form).toBeDirty();
expect(input).toBeDirty();
formCtrl.$setPristine();
expect(form).toBePristine();
expect(formCtrl.$pristine).toBe(true);
expect(formCtrl.$dirty).toBe(false);
expect(input).toBePristine();
expect(inputCtrl.$pristine).toBe(true);
expect(inputCtrl.$dirty).toBe(false);
});
it('should reset pristine state of nested forms', function() {
doc = $compile(
'<form name="testForm">' +
'<div ng-form>' +
'<input ng-model="named" name="foo">' +
'</div>' +
'</form>')(scope);
scope.$digest();
var form = doc,
formCtrl = scope.testForm,
nestedForm = form.find('div'),
nestedFormCtrl = nestedForm.controller('form'),
nestedInput = form.find('input').eq(0),
nestedInputCtrl = nestedInput.controller('ngModel');
nestedInputCtrl.$setViewValue('xx');
scope.$apply();
expect(form).toBeDirty();
expect(nestedForm).toBeDirty();
expect(nestedInput).toBeDirty();
formCtrl.$setPristine();
expect(form).toBePristine();
expect(formCtrl.$pristine).toBe(true);
expect(formCtrl.$dirty).toBe(false);
expect(nestedForm).toBePristine();
expect(nestedFormCtrl.$pristine).toBe(true);
expect(nestedFormCtrl.$dirty).toBe(false);
expect(nestedInput).toBePristine();
expect(nestedInputCtrl.$pristine).toBe(true);
expect(nestedInputCtrl.$dirty).toBe(false);
});
});
});

View file

@ -117,6 +117,18 @@ describe('NgModelController', function() {
});
});
describe('setPristine', function() {
it('should set control to its pristine state', function() {
ctrl.$setViewValue('edit');
expect(ctrl.$dirty).toBe(true);
expect(ctrl.$pristine).toBe(false);
ctrl.$setPristine();
expect(ctrl.$dirty).toBe(false);
expect(ctrl.$pristine).toBe(true);
});
});
describe('view -> model', function() {