Reputation: 15481
I use ctrl.$setValidity
within one of my directives to invalidate a form. However, there is a method residing elsewhere which could remove this element from the DOM under a different set of conditions. What happens when I invalidate the form with $setValidity
and then remove the offending element is that the form remains invalid, while what I would like it to do is to recalculate its validity based on its new Inputfield set.
Please note that I am not simply looking for a ctrl.$setValidity true
, (other input fields in the form may or may not be valid), I simply want the form to recalculate.
Below is the directive code:
app.directive('dir', function() {
return {
restrict: 'C',
require: "ngModel",
link: function(scope, element, attrs, ctrl) {
someValue = scope.someValue;
someField = element.find(".some-class");
monitorField = function(newValue, oldValue){
console.log("whee");
scope.someFunction(newValue);
if(newValue =="Invalidate NOW!"){
ctrl.$setValidity('someClass', false);
} else if (oldValue == "Invalidate NOW!"){
angular.element(document.querySelector('.some-class')).remove();
} else {
ctrl.$setValidity('someClass', true);
}
}
scope.$watch("someValue", monitorField, true);
}
}
});
Which operates on:
<FORM class="dir" ng-model="someValue">
<INPUT type="text" class="some-class" ng-model="someValue"/>
<INPUT type="text" class="some-other-class"/>
</FORM>
(Yes it is a somewhat contrived example).
The problem is reproduced here: http://jsfiddle.net/kTuAY/
In my actual code, I populate the input fields based on an array of objects, which gets populated via Service, and remove elements via array.splice. The example given in the jsfiddle is merely there for simplicity.
Another interesting condition of failure can be seen in this fiddle:
Specifically, if one of the inputs depends on non collision of value with the other, and is thusly invalidated, whereafter the other fields value is changed, validity remains incorrect.
Temporary not quite functioning fiddle while I go to work:
Thanks!
Upvotes: 2
Views: 504
Reputation: 15481
The way I ended up solving this was:
ng-repeated
over..val()
, and then call $compile
on the inner fields to retrigger the inner watch expressionctrl.$setValidity
from the inner directive:The good:
$digest
's is doubledI'll put together a code example just as soon as I sanitize the code.
Upvotes: 0
Reputation: 108501
Removal of a DOM element that is linked to part of your model should be done by removing the part of the $scoped object the model is pointing at if possible. Think of the basic ngRepeat, if you remove an item from the array, it removes an element from the DOM. Your form should work the same way. This will prevent that from happening. All of the validation information is contained on the model in your $scope... like so: $scope.myForm.model.$error.required
for example. If you remove the $scope property or array item that the model is pointing at (e.g. $scope.foo
or $scope.items.splice(2,1)
, Angular will know (presumably during the next $digest) to get rid of the model as well as it's validation information.
It doesn't matter if you remove the item in a controller or in a directive, but it should be removed from the $scope, then a digest needs to occur for the UI and validation to update.
I hope that helps, because I feel like I stumbled over what I was trying to say...
Upvotes: 2
Reputation: 7134
I do not think the problem is in the directive, it is in the 'other' code
If the DOM element removal is done n response to 'native' events - i.e. drag'n drop events, ajax events or something, the angular framework would not know to run the recalculation by itself. In this code after you are done updating you have to call $scope.$apply() on a proper scope.
Oh I see your fiddle... ok. How about making life a little easier - instead of removing your DOM element why do not you decorate it with a ng-show attribute and let angular take care of the rest?
Upvotes: 0