EDIT: Forked @EliteOctagon's plunker and oddly it's working! Can't understand why the code below isn't.
EDIT2: Forked the previous plunker and added a $timeout to controller's logic and it stopped working! Guess it's really loading order. Check it out:
I'm new to angular and can't get my head wrapped around directive isolate scopes.
I need to create a directive to print out a <span/>
in my page with info regarding and object in the view controller.
What I was trying to do is isolate the directive scope and pass the object through an attribute with two-way binding (see code below). However, when trying to access the object in the link
function of the directive it always comes out as undefined.
What am I missing here?
Thank you in advance guys.
Directive use in html view template:
<party-starts party="party"></party-starts>
Directive JS code
.directive('partyStarts', function(){
return {
restrict: 'E',
template: '<div id="partyStart><i class="icon ion-pin"></i> </div>',
scope: {
party: '='
},
link: function(scope, el) {
var party = scope.party;
var icon = el.find('i');
var statusStr = angular.element('<span/>');
var final;
console.log('scope '+scope);
if(party.Diatodo){
if(party.datahora.isSame(moment(), 'day') || party.datahora.isSame(moment().add(1, 'd'), 'day')){
icon.css({
'color': 'green'
});
statusStr.text(' É hoje');
el.append(statusStr);
}else{
icon.css({
'color': '#999'
});
statusStr.text(' Começa em '+party.datahora.fromNow());
el.append(statusStr);
}
return;
}
if(party.datahora.unix() == party.datahoraf.unix()){
final = party.datahora.clone().add(1, 'd').hour(6);
}else{
final = party.datahoraf;
}
if(party.datahora.twix(final).isCurrent()){
icon.css({
'color': 'green'
});
statusStr.text(' Começou há '+party.datahora.fromNow());
el.append(statusStr);
}else if(party.datahora.twix(final).isFuture()){
icon.css({
'color': '#999'
});
statusStr.text(' Começa em '+party.datahora.fromNow());
el.append(statusStr);
}else{
icon.css({
'color': 'red'
});
statusStr.text(' Já terminou');
el.append(statusStr);
}
}
};
})
EDIT: Forked @EliteOctagon's plunker and oddly it's working! Can't understand why the code below isn't. http://plnkr.co/edit/y8uvulA9RHQ1Y9mwzin1
EDIT2: Forked the previous plunker and added a $timeout to controller's logic and it stopped working! Guess it's really loading order. Check it out: http://plnkr.co/edit/ivmGQmEHTatNzBWhppyf
I'm new to angular and can't get my head wrapped around directive isolate scopes.
I need to create a directive to print out a <span/>
in my page with info regarding and object in the view controller.
What I was trying to do is isolate the directive scope and pass the object through an attribute with two-way binding (see code below). However, when trying to access the object in the link
function of the directive it always comes out as undefined.
What am I missing here?
Thank you in advance guys.
Directive use in html view template:
<party-starts party="party"></party-starts>
Directive JS code
.directive('partyStarts', function(){
return {
restrict: 'E',
template: '<div id="partyStart><i class="icon ion-pin"></i> </div>',
scope: {
party: '='
},
link: function(scope, el) {
var party = scope.party;
var icon = el.find('i');
var statusStr = angular.element('<span/>');
var final;
console.log('scope '+scope);
if(party.Diatodo){
if(party.datahora.isSame(moment(), 'day') || party.datahora.isSame(moment().add(1, 'd'), 'day')){
icon.css({
'color': 'green'
});
statusStr.text(' É hoje');
el.append(statusStr);
}else{
icon.css({
'color': '#999'
});
statusStr.text(' Começa em '+party.datahora.fromNow());
el.append(statusStr);
}
return;
}
if(party.datahora.unix() == party.datahoraf.unix()){
final = party.datahora.clone().add(1, 'd').hour(6);
}else{
final = party.datahoraf;
}
if(party.datahora.twix(final).isCurrent()){
icon.css({
'color': 'green'
});
statusStr.text(' Começou há '+party.datahora.fromNow());
el.append(statusStr);
}else if(party.datahora.twix(final).isFuture()){
icon.css({
'color': '#999'
});
statusStr.text(' Começa em '+party.datahora.fromNow());
el.append(statusStr);
}else{
icon.css({
'color': 'red'
});
statusStr.text(' Já terminou');
el.append(statusStr);
}
}
};
})
Share
Improve this question
edited Nov 12, 2014 at 19:05
GNSPS
asked Nov 12, 2014 at 18:29
GNSPSGNSPS
852 silver badges9 bronze badges
5
- can you put up a plunker with some code? here is a blank template plnkr.co/edit/TkONdJqtD6nSg6s0Da59 – SoluableNonagon Commented Nov 12, 2014 at 18:34
- As long as $scope.parent is defined inside the parent controller, there should be no issue. Are you possibly using party-starts in an ng-if or ng-repeat, and therefore unintentially creating a new child scope? – Zack Argyle Commented Nov 12, 2014 at 18:45
- no ng-repeat loops here, just a regular object in the controller scope being passed – GNSPS Commented Nov 12, 2014 at 18:49
- What does the parent controller look like with $scope.party? – Zack Argyle Commented Nov 12, 2014 at 18:51
- @EliteOctagon just forked your plunker with equivalent code and it just works... Can't understand it at all :( plnkr.co/edit/y8uvulA9RHQ1Y9mwzin1 – GNSPS Commented Nov 12, 2014 at 18:55
3 Answers
Reset to default 9Wrap the directive's party scope variable in a watcher and wait for it to be initialized. In your case you don't do this and so all your logic is being run before scope.party is assigned.
link: function(scope, el) {
var watcher = scope.$watch('party', function() {
if(scope.party === undefined) return;
// at this point it is defined, do work
// delete watcher if appropriate
watcher();
})
}
I don't think this is an issue with load order. From what I can tell the code you provided should function. Here is a plunker with a working example:
http://plnkr.co/edit/Bsn3Vyjrmb311b8ykzU1 There is also logging to the console, which verifies that scope.party
is initialized with the correct value on load.
Click on the live preview (the Eye Icon on the right sidebar) to view the example.
Also, here is a diagram that showed up in my twitter feed the other day that I found very useful for explaining isolate scope in an understandable way. Hope this helps :-)
In my case, the reason why it was undefined was because the timing was incorrect. Say you have a directive that shows a list of products:
<my-products products='httpLoadedProducts'>/<my-products>
And you define it like so:
.directive('myProducts', function () {
return {
restrict: 'E',
scope: {
products: '=',
},
templateUrl: 'directives/products-template.html',
link: function (scope, element, attrs) {
console.log(scope.products);
}
}
})
If the list of products comes from an HTTP request, it is very likely that the directive will be instantatied before the products are loaded, therefore the console.log
prints undefined
.
One solution is to instantiate the directive after the items are loaded. This will ensure that the link
function runs after the items are loaded.
So, either wrap the directive with an ng-if
, like this:
<div ng-if='httpLoadedProducts'>
<my-products products='httpLoadedProducts'>/<my-products>
</div>
Or use wbeange's answer to watch the model for changes.