Reputation: 1106
I'm writing a directive with custom validation logic to validate an object.
HTML:
<input type="hidden" name="obj" ng-model="vm.obj" validate-object />
JS:
angular
.module('myApp')
.directive('validateObject', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$validators.validateObject = myValidator;
function myValidator (modelValue, viewValue) {
return validateObject(modelValue);
}
function validateObject (obj) {
// Look inside the object
}
}
}
});
The problem is that the validator doesn't run when a property inside the object is changed.
I could add a $watch
with objectEquality === true
, and then manually $setCustomValidity
with my validation logic. Something like this:
link: function (scope, element, attrs, ngModelCtrl) {
scope.$watch(attrs.ngModel, onModelChange, true);
function onModelChange (newValue) {
ngModelCtrl.$setCustomValidity('validateObject', validateObject(newValue))
}
function validateObject (obj) {
// Look inside the object
}
}
But I don't like using the old school way of manually using $setValidity
, plus adding a manual $watch
while NgModelController
already has ways of registering inside the update process (like $formatters
), and in addition the $watch
being a deep one which can has performance issues.
Am I getting this wrong? Is there a better way?
Upvotes: 1
Views: 973
Reputation: 359
From https://github.com/angular/angular.js/blob/master/src/ng/directive/ngModel.js#L699 :
if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
return;
}
ngModel performs a flat equality check against the older version of the model, so any changes inside an object would not be reflected on ngModel or ngChange.
The perferred approach would be to use immutable data, that means that every time you change the model (the object), create a new copy instead:
function changeModel(){
this.vm.name = "roy";
// Create a new object for ngModel;
this.vm = angular.copy(this.vm);
}
EDIT
I remember that I solved a previous issue before. You want to have a set of ng-models binded to properties on an object, and have 1 change listener for the entire object.
Here's my solution: http://plnkr.co/edit/6tPMrB8n1agINMo252F2?p=preview
What I did was to create a new directive "formModel" that must be placed on a form element. Angular has a form directive which has a controller. NgModelController requires a parent form controller, which then it adds itself to the form (this is how you get validity on an entire form). So in my directive, I decorated the form's $addControl method, and added a listener for every ngModelController that adds itself via $viewChangeListeners, and now on every change of ngModel inside the form, the formModel directive will duplicate the entire object and fire $setViewValue.
Upvotes: 2