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?
ngModel is a directive which binds input, select and textarea, and stores the required user value in a variable and we can use that variable whenever we require that value. It also is used during validations in a form.
The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.
NgModel expects the bound element to have a value property, which div s don't have. That's why you get the No value accessor error. I don't know if the input event is supported on all browsers for contenteditable . You could always bind to some keyboard event instead.
Yeah, you can use a function. In which case the ng-model will be the value returned by the function.
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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With