How to Watch Directive's Directive ng-model
As @shaunhusain mentioned you have to use the ngModelController to interact with ngModel. On the ngModelController you can set up a watch on the $modelValue
and you can change the value in the model by calling $setViewValue
. Remeber that to use the ngModelController you need to add a require: "ngModel"
to your directive definition object.
When you get a value from outside of angular (like from your database) and you in turn want to use that value to change the model value, you need to wrap that code in a scope.$apply()
app.directive('thisDirective', function($compile, $timeout, $log) {
return {
scope: false,
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
...
scope.$watch(
function(){
return ngModel.$modelValue;
}, function(newValue, oldValue){
$log.info('in *thisDirective* model value changed...', newValue, oldValue);
}, true);
}, // end link
} // end return
});
app.directive('childDirective', function($compile, $timeout, $log) {
return {
scope: {
ngModel: '='
},
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
...
scope.$watch(
function(){
return ngModel.$modelValue;
}, function(newValue, oldValue){
$log.info('in *childDirective* model value changed...', newValue, oldValue);
}, true);
// make believe change by server
setTimeout(function() {
scope.$apply(function() {
ngModel.$setViewValue('set from the server...');
};
},5000);
},
} // end return
});
relevant jsfiddle http://jsfiddle.net/CnDKN/2/
BUT I don't think this is not really right usage of $setViewValue
. According to the docs, that is supposed to be used to update the value that is shown on the UI, not necessarily the model value.
There is actually another way of making this work which I think is more straightforward and easier to use. Just use =attr
to set up bi-directional binding of the property you want to use in both thisDirective
and in childDirective
. And then you can just set up the the ng-model attribute setting in your directive and when you first use it you don't even need to know that it is using ng-model underneath.
Here is the code that shows you what I mean:
app.directive('thisDirective', function($compile, $timeout, $log) {
return {
scope: {
thisval: '='
},
link: function(scope, element, attrs) {
var htmlText = '<input type="text" ng-model="thisval" />' +
'<div child-directive childval="thisval"></div>';
$compile(htmlText)(scope, function(_element, _scope) {
element.replaceWith(_element);
});
scope.$watch('thisval',function(newVal,oldVal){
$log.info('in *thisDirective* thisval changed...',
newVal, oldVal);
});
}, // end link
} // end return
});
app.directive('childDirective', function($compile, $timeout, $log) {
return {
scope: {
childval: '='
},
link: function(scope, element, attrs) {
var htmlText = '<input type="text" ng-model="childval" />';
$compile(htmlText)(scope, function(_element, _scope) {
element.replaceWith(_element);
});
scope.$watch('childval',function(newVal,oldVal){
$log.info('in *childDirective* childval changed...',
newVal, oldVal);
});
// make believe change that gets called outside of angular
setTimeout(function() {
// need to wrap the setting of values in the scope
// inside of an $apply so that a digest cycle will be
// started and have all of the watches on the value called
scope.$apply(function(){
scope.childval = "set outside of angular...";
});
},5000);
},
} // end return
});
Updated jsfiddle example: http://jsfiddle.net/CnDKN/3/
Neel
Previously know as @blackops_programmer. I know, it was a pretty lame Display name.
Updated on June 08, 2022Comments
-
Neel about 2 years
I have a directive that uses the parent scope in that view. This directive has a child directive that uses an isolated scope. I am trying to get the parent directive to watch any changes done to the ngModel of the child directive and update its own modal if changes were made. Here is a jsfiddle that probably explains better: http://jsfiddle.net/Alien_time/CnDKN/
Here is the code:
<div ng-app="app"> <div ng-controller="MyController"> <form name="someForm"> <div this-directive ng-model="theModel"></div> </form> </div> </div>
Javascript:
var app = angular.module('app', []); app.controller('MyController', function() { }); app.directive('thisDirective', function($compile, $timeout) { return { scope: false, link: function(scope, element, attrs) { var ngModel = attrs.ngModel; var htmlText = '<input type="text" ng-model="'+ ngModel + '" />' + '<div child-directive ng-model="'+ ngModel + '"></div>'; $compile(htmlText)(scope, function(_element, _scope) { element.replaceWith(_element); }); // Not sure how to watch changes in childDirective's ngModel ??????? }, // end link } // end return }); app.directive('childDirective', function($compile, $timeout) { return { scope: { ngModel: '=' }, link: function(scope, element, attrs, ngModel) { var htmlText = '<input type="text" ng-model="ngModel" />'; $compile(htmlText)(scope, function(_element, _scope) { element.replaceWith(_element); }); // Here the directive text field updates after some server side process scope.ngModel = scope.dbInsertId; scope.$watch('dbInsertId', function(newValue, oldValue) { if (newValue) console.log("I see a data change!"); // Delete this later scope.ngModel = scope.imageId; }, true); }, } // end return });
In the example, you can see that there is a text input inside a parent directive as well as its child directive. If you type inside each of them, the other model gets updated since they are binded by
ngmodel
. However, the child directive's text input gets updated after a server connection. When that happens, the text input in the parent directive doesnt get updated. So I think I need to watch the ngModel inside the child directive for any changes. How can I do that? Does it make sense? -
Neel over 10 yearsExcellent Answer! Learnt so much from it! Thank you @JoseM :)
-
Neel over 10 yearsHi JoseM, After your awesome answer and support, I just put together what I learnt so far. I now understand how and when to use the
scope.$appy()
method. I was struggling with that. I also find it much easier to have an isolated scope, pass theng-model
and use that to watch and update the model. Your second example helped alot with that. One of the biggest challenge was the$apply
. What was happening was just setting the$watch
updated the ng-model in child directive but the parent directive was not aware of the change. .... -
Neel over 10 years... Thats why the scope in parent didint get updated although they were binded by ngModelController. Since it was an ajax server side update, adding the
$apply
method updated the scope and the parent directive became aware of this change. It all worked in the end. phew... what a learning curve it was. Being new to$apply
, I had a tough time in understanding how and where to use it and there wasnt too much examples on this. Now it all makes sense. Thank you so much for your help... It feels so good once you learn the Angular magic after being lost in wilderness! :) -
JoseM over 10 years@blackops_programmer check out the egghead.io videos at egghead.io/technologies/angularjs to help you understand AngularJS