$watch in a link function of my directive is not updated - angularjs

I have this directive which tries to watch for changes in a json object on scope. The JSON object is retrieved using a restangular based service, but somehow the $watch seems to be executed only once, logging 'undefined'.
The directive is used in the index.html of the app, so I suspect this has to do with the controller only working for the specific view or form...is there a way to get the directive to see those changes?
update: figured I could just call the TextsService from the directive itself, seems like a good solution to the problem. If anyone has better suggestions I'd welcome them still though.
service:
angular.module('main').service('TextsService', function(Restangular) {
this.getTexts = function(jsonRequestBase, jsonRequest, callback) {
Restangular.one(jsonRequestBase, jsonRequest).get().then(
function(texts) {
callback(texts);
}
);
};
});
call in controller:
TexstService.getTexts("content", "file.json", function (texts) {
$scope.mytest = texts;
});
directive:
app.directive('myDirective',
function() {
return {
restrict: 'A',
templateUrl:'test.html',
transclude: true,
link: function(scope, elm, attrs) {
scope.$watch('mytest', function(){
console.log(scope.mytest);
}, true);

I figured I could just call the TextsService from the directive itself, seems like a good solution to the problem. If anyone has better suggestions I'd welcome them still though.

Create the variable with null before receving it. Maybe that works.

The problem is that your directive's scope does not know about the mytest variable. You need to define the binding when you define the directive:
app.directive('myDirective',
function() {
return {
scope: {
myDirective: '='
},
restrict: 'A',
templateUrl:'test.html',
transclude: true,
link: function(scope, elm, attrs) {
scope.$watch('myDirective', function(newVal){
console.log(newVal);
}, true);
};
}
);
This will get whatever variable is assigned to the directive attribute and watch its' value.
And your directive in the view should look like this to bind it to the 'mytest':
<div my-directive="mytest"></div>

Related

binding data in a directive returns inconsistent values

i have this wierd problem with binding data to a directive. this is how i declare my directive:
<my-directive data="myArray"></my-directive>
my directive code looks like:
angular.module('ngApp')
.directive('myDirective', function () {
return {
scope:{
data: '='
},
template: '<div steps="data.length"></div>',
restrict: 'E',
link: function postLink(scope, element, attrs) {
console.log(scope);
console.log(scope.data);
}
};
});
in the first log, the data property is correct:
screenshot of console.log output
but the second log is undefined.
any idea why?
here is your solution :
<my-directive data-example="myArray"></my-directive>
you should always name your variables data-<name>. This is far easier to maintain and this prevent errors with keywords like this one ;)
angular.module('ngApp')
.directive('myDirective', function () {
return {
scope:{
data: '=example'
},
template: '<div steps="data.length"></div>',
restrict: 'E',
link: function postLink(scope, element, attrs) {
console.log(scope);
console.log(scope.data);
}
};
});
i hope this help,
Live example : JsFiddle
thanks to #hadiJZ and #Unex i figured it out:
my directive was nested in another directive. but the parent directive used the link function for the logic, since on creation it wasn't having child directives.
so moving the logic out to a controller solved my problem.
i figured it out when i added a $timeout to the child directive, and the log (scope.data) was correct.

Unable to share controller instance via require in angular directive

I have a confirm box which I want to attach another directive to called confirmBoxToggle, but I'm unable to share the same controller instance in order for it to work. I've looked at multiple examples and also read the docs to see if I'm doing something crazy, but the only thing I can see is that I don't declare my controller inside the directive but rather giving a reference to it. But I can't see this being the issue.
I get this error when doing this:
Controller 'confirmBox', required by directive 'confirmBoxToggle', can't be found!
What am I doing wrong?
The box directive:
core.directive('confirmBox', [function() {
return {
scope: {},
controller: 'ConfirmBoxCtrl',
controllerAs: 'confirmBox',
templateUrl: 'app/views/components/core/confirmation-box.html',
link: function(scope, element, attrs, ctrl) {
}
};
}]);
The toggle directive:
core.directive('confirmBoxToggle', [function() {
return {
scope: {},
require: '^confirmBox',
link: function(scope, element, attrs, ctrl) {
element.on('click', function() {
ctrl.toggleBox();
});
}
};
}]);
The controller for both directives:
core.controller('ConfirmBoxCtrl', [function() {
var confirmBox = this;
confirmBox.toggleBox = function() {
confirmBox.isActive = !confirmBox.isActive;
};
}]);
I use the directives like this:
<confirm-box></confirm-box>
<span confirm-box-toggle>Delete</span>
Controller confirm or confirmBox can't be found?
Do you use that controller elsewhere, and does it work on it's own?
Basically you used require: '^confirmBox' that means while using confirmBoxToggle directive, it must be wrap with confirmBoxdirective(should be there in parent element as ^) so that you could access to the confirmBox link function 4th parameter.
HTML
<confirm-box>
<span confirm-box-toggle>Delete</span>
</confirm-box>
Also you can't have templateUrl inside your confirmBox directive, which will replace your <span confirm-box-toggle>Delete</span> html by the template loaded form templateUrl.
Demo Plunkr

'attrs' property in angular directive is not working properly

I am trying to write a very simple directive which involves setting a property based off one of the attributes that it is provided. The issue I'm facing is that the value of the attrs object is not being consistently recognized within the link function.
Here is the grand total of what I'm currently trying to achieve :
angular.module('directives').directive('wikiNotes',function() {
return {
restrict: 'EA',
templateUrl: 'common/directives/wiki-notes.tpl.html',
link: function(scope, element, attrs) {
console.log(attrs.openEdit); //true
if(attrs.openEdit===true){
console.log('open edit'); //not called
}
}
};
});
The console.log(attrs.openEdit) is showing as true, but then the console.log in the if block is not getting called. Am I missing something very obvious or is this a quirk with angular directives?
Did you consider adding this attribute in your directive scope section?
I think it is more in the Angular philosophy, but this imply you create a new scope for your directive.
angular.module('directives')
.directive('wikiNotes',function() {
return {
restrict: 'EA',
scope: {
openEdit: '='
},
templateUrl: 'common/directives/wiki-notes.tpl.html',
link: function(scope) {
console.log(scope.openEdit); //true
if(scope.openEdit===true){
console.log('open edit'); //should be a boolean
}
}
};
});
Here is a JS fiddle that demonstrate it works.
https://jsfiddle.net/c8mn9wka/3/

Binding global scope variable to directive local variable

I'm having trouble understanding scope linking between controllers and directives.
What I'm trying to do (which should help me learn a lot) is bind $scope.systems in my controller to data in my directive.
So I setup a simple directive call:
<combobox data="systems"></combobox>
I also tried binding the variable, but it didn't make sense to me.
<combobox data="{{systems}}"></combobox>
Then I created my driver as such
.directive('combobox', function ($timeout) {
return {
restrict: 'E',
replace: true,
templateUrl: '/angular/directives/combobox.php',
link: function (scope, element, attrs) {
console.log(attrs.data);
$timeout(function () {
console.log(scope.systems);
console.log($scope[attrs.data]);
}, 1000);
}
}
});
I considered adding a scope parameter to the directive return
scope: {
'systems': '='
}
Or
scope: {
'systems': '=data'
}
I've been able to setup simple directives where values are bound to the directive scope, and they've worked. Now I'm trying to create a reusable directive where I can tell it what data from the controller scope to use, and I'm just stuck.
This should work. Although I'm not sure why your template is a php file...
<combobox data="foo"></combobox>
<combobox data="bar"></combobox>
app.directive('combobox', function ($timeout) {
return {
restrict: 'E',
replace: true,
scope: {
//this will set $scope.systems
//with the value gotten from evaluating what is in
//the data attribute
'systems': '=data'
},
templateUrl: '/angular/directives/combobox.php',
link: function (scope, element, attrs) {
console.log(scope.systems);
}
}
});
BTW, don't use replace. The Angular team said it will probably disappear soon because it is causing too many issues and is not that necessary anyway.

angular directive - scope undefined inside function

I can't seem to reach the link function scope variable from inside a function in my directive. The "elem" variable is defined, but the scope isn't. Why is that??
Here's my directive:
function contextMenu($document){
return {
scope: {
target: '=',
},
restrict: 'A',
link: function(scope, elem, attr) {
elem.bind('contextmenu',handleRightClick);
function handleRightClick(event) {
// do something with scope (scope is undefined)
}
}
}
};
How can I user the scope variable?
Thanks!
Uri
EDIT1:
I found I can use this to pass the scope to the function:
Passing parameters to click() & bind() event in jquery?, but this still doesn't explain why the scope variable is undefined.
EDIT2:
For completeness sake, this is how my directive is set up:
app.js
angular
.module('myModule', [])
.directive('contextMenu', ['$document', components.contextMenu])
and in the html:
<div context-menu target="testObject">
Make sure you are using the directive correctly. Since you didn't include the use of the directive in your template, I hope you used it something like this:
<div context-menu target="something"></div>
Then I am confused about the setup of your directive. Try this:
MyDirectiveModule.directive('contextMenu', function(){
return {
restrict: 'A',
scope: {
target: '#'
},
link: function(scope, element){
console.log(scope);
// don't use $scope!
}
};
});
Make sure to use scope instead of $scope in the link: part.

Resources