6

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);
            }
        }
    };
})
5
  • can you put up a plunker with some code? here is a blank template plnkr.co/edit/TkONdJqtD6nSg6s0Da59 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? Commented Nov 12, 2014 at 18:45
  • no ng-repeat loops here, just a regular object in the controller scope being passed Commented Nov 12, 2014 at 18:49
  • What does the parent controller look like with $scope.party? 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 Commented Nov 12, 2014 at 18:55

3 Answers 3

9

Wrap 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();
  })
}
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you! I guess you're right. Because I've wrapped the other plunker in a timeout and it stops working. plnkr.co/edit/ivmGQmEHTatNzBWhppyf
$timeout could make it work if the timeout was long enough, but use a $watch instead of a $timeout, because you can't define time.
Yes, that was it! Thank you very much see updated plunker in EDIT2
I was just wrapping the controller logic in $timeout to test for your hypothesis, not to solve the problem, for that I used $watch as you suggested! :)
@wbeange why are you wrapping $watch with custom function and calling itself repeatedly? Isn't $watch already doing it anyway?
|
9

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 :-)

enter image description here

1 Comment

Awesome infographic, really useful! Thanks! :D Can't vote up (not enough rep) but I'll leave it here +1 :)
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.