I think you may have uncovered a weird bug here. Typically resetting the model will also update all validation.And it actually does internally in the form and ng-model controllers, but for some reason it’s not updating the dom until the next digest cycle.
Take a look at this plunkr:
http://plnkr.co/edit/G8up5ESbbCsLd4Mtjoj9?p=preview
Notice how “Form valid” updates as you enter things into the form or delete them.
I have severl ways to reset the form model:
Bad fill just calls a method that does $scope.name="Hello". If you click on it the form field will update but it will still display “Form Valid: false” If you click it twice it will update “Form Valid: true”
Working Fill link does the same as Bad Fill but sets up a $timeout(funciton(){},0) after setting the model value. This will force another $digest() cycle after the current thread is done executing. This makes things work.
Works With Button button calls the exact same method as the Bad Fill link, but this updates “Form Valid: true”, I’m guessing some weird behavior is triggering extra digest cycles with a button that aren’t triggered with the link.
You can work around your issue with the $timeout(function(){},0) trick after you reset your model. However you shouldn’t have to do this.
I will do some more research to find out where the problem is and see if I can log an issue and a PR if there isn’t one pending already.
No. $setPristine() is not at all related to validation. It simply resets the $dirty and $pristine flags and removes “ng-dirty” class from the form elements and adds “ng-pristine” class.
Validations cleared automatically when the model changes, except that the template is sometimes not immediately updated due to the bug above.
If you are not seeing this behavior then create a plunkr and I can help you sort it out.
There are many work-arrounds to the bug listed above. As long as any other watch triggers during the digest cycle it should work, so you could force it by adding a dummy div at the end of your page:
<div ng-show="myForm.$valid"></div>What you could do is set an event listener on your validation directive and then broadcast an event on your form when you need to reset. Then call $setValidity(true) on the ngmodel. Here’s an example:
scope.$on('resetForm', function(event, form){
//Only reset if resetForm event intended for this form
if(formCtrl===form){
ngModel.$setValidity('invalid', true);
delete ngModel.$error.message
}
});
and then you can have your reset method broadcast:
app.controller('testCtrl', function ($scope) {
$scope.someval = 't';
$scope.reset = function (form) {
$scope.$broadcast('resetForm', form);
}
})
You can see the full example here:
http://plnkr.co/edit/wmsRNqnKS6FCCa9jBjDk?p=preview
Be aware that the way you are doing validation would only work if you make your own validation directives. If you used standard validation such as adding ‘required’ angular will validate in real-time without waiting for a submit.
In addition I thought I should note a couple of changes I did to your plunkr:
The proper way to pass in the form controller to your directive is through require, not getting it from the dom:
require: ['ngModel', '^form'],
link: function (scope, elem, attrs, controllers) {
var ngModel = controllers[0];
var formCtrl = controllers[1];
...
}
You should not wrap elem in a jquery object (e.g. $(elem)). Rather just make sure the jquery script tag is included BEFORE angular uses jquery instead of jqLite. You can then just use jQuery methods directly off of element without wrapping.
BTW, thanks for pointing out the jquery bit and the ngform controller best practices.
The problem with adding a method like this to the form is that it clashes with the built-in way that angular does validation. The angular approach is to have validation happen in real time through the parsers and formatters of the ngModelController in each input. In this context, clearing or resetting the validation on the form doesn’t make sense because then the validation state would be out of sync with reality. I still think even if you are not using the “live” validation, validity should still be something that you set on an individual control and not the entire form.
The issue here is how your custom validation directives should get notified that they should reset themselves . Event listeners is not too bad of a solution.