I've implemented a custom input directive - counter
with a reset capability. The directive has require: "ngModel"
.
I am resetting the pristine state of the directive's ngModel
with $setPristine()
. Unlike $setDirty()
, $setPristine()
does not touch the $pristine
state of the parent form.
Q: How do I "notify" the parent form that this directive is no longer "dirty", such that the parent form could have its $pristine
state reset?
Bear in mind that just calling form.$setPristine()
is not enough as there may be other "dirty" controls in the form, which my directive wouldn't (and shouldn't) know about.
This is the directive's link function:
link: function(scope, element, attrs, ngModel){
var original;
ngModel.$render = function(){
original = scope.counter = ngModel.$viewValue;
};
scope.up = function(){
ngModel.$setViewValue(++scope.counter);
};
scope.reset = function(){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine(); // this sets $pristine on the directive, but not the form
};
}
And here's how it is used:
<div ng-form="form">
<counter ng-model="count"></counter>
</div>
As of Angular 1.3.x, there is no built-in solution.
form.$setPristine()
sets pristine on all its child controls. (link to code)
ngModel.$setPristine()
only sets $pristine on itself (link to code)
One way to solve this is to create a directive that lives alongside a form
directive and hijacks form.$setDirty
to track dirty controls count. This is probably best done in a pre-link phase (i.e. before child controls start registering themselves).
app.directive("pristinableForm", function() {
return {
restrict: "A",
require: ["pristinableForm", "form"],
link: function(scope, element, attrs, ctrls) {
var me = ctrls[0],
form = ctrls[1];
me.form = form;
me.dirtyCounter = 0;
var formSetDirtyFn = form.$setDirty;
form.$setDirty = function() {
me.dirtyCounter++;
formSetDirtyFn();
};
},
controller: function() {
this.$notifyPristine = function() {
if (this.dirtyCounter === 0) return;
if (this.dirtyCounter === 1) {
this.dirtyCounter = 0;
if (this.form) this.form.$setPristine();
} else {
this.dirtyCounter--;
}
};
}
};
});
Then, the custom input directive needs to require: ["ngModel", "^pristinableForm"]
and call pristinableForm.$notifyPristine()
in its reset function:
scope.reset = function(){
if (ngModel.$dirty){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine();
pristinableForm.$notifyPristine();
}
};
The usage is:
<div ng-form="form" pristinable-form>
<counter ng-model="count1"></counter>
<counter ng-model="count2"></counter>
<input ng-model="foo" name="anotherControl">
</div>