AngularJS scope values undefined in link

2019-06-25 10:02发布

问题:

I'm trying to figure out something with scope and link when a directive is initialized. I have a directive in a tree controller to display details at a branch point:

<typelists sub="branch.subBranches[0]"></typelists>

The (relevant parts of the) directive that handles that branch info are below:

listsApp.directive(
    'typelists',
    function($rootScope) {
        return {
            restrict: 'EA',
            replace: true,
            scope: {
                branch : '=sub'
            },
            templateUrl: templateDir + '/typelists.html',
            link: function (scope, element, attrs) {
                console.log(scope,scope.branch); // DEBUG

                // other stuff
                //  (including a working reload() and refresh() methods)

                scope.subject = 'Type' + scope.branch.model.getFilter() + 'Lists';

                // catch $rootScope.$broadcasts for this branch
                $rootScope.$on( scope.subject+'Refresh', function() { scope.refresh(); ) } );
                $rootScope.$on( scope.subject+'Reload', function() { scope.reload(); } );
            }
        };

Now, what is confusing the bajeezus out of me is that in the // DEBUG line, I can see .branch populated as expected in the output of the scope alone, but scope.branch shows as undefined. This means that when I try to set scope.subject down below, instead of getting a typeId back from the parent type, I'm getting 'undefined' so instead of getting a unique branch tag such as 'Type1Lists' I'm getting 'TypeundefinedLists', thus my $on watch functions aren't triggering properly.

Why am I unable to access scope.branch or why is it showing as undefined when I try? (especially when I can see it in the same console.log output as part of scope?)

Thanks in advance for any help.

回答1:

How does branch.subBranches[0] get populated? I bet that value is set just after the link function of the directive runs.

You can either make the directive resilient to these changes, like so:

var unwatch = scope.$watch("scope.branch", function(v){
  if (v) {
    unwatch(); // removes the watch when value is not undefined
    init(); // run your init code
  }
});

Or, only instantiate the directive when the data is ready:

<typelists ng-if="branch.subBranches[0] !== undefined" 
           sub="branch.subBranches[0]"></typelists>

P.S.

The reason console.log shows the data is because (at least in Chrome) the console "rendering" doesn't happen at the time of logging - in other words, when you call console.log the data is still not there, but it gets there before the console reads it for rendering purposes.



回答2:

I would bet this is happening because you are setting branch.subBranches[0] in a parent directives link function.

However Angular links directives in a bottom-up manner. Namely the child directives link function will be called BEFORE the parents. Hence if you are setting 'branch.subBranches[0]' in the parent directives link function then it will still be undefined when the child directives link function is run (run first).

The timing of Angular directive DOM compilation is such that the controllers are run first from top-bottom (parent first), and then linked back up bottom-top (parent last).

So to fix your problem the easiest way would be to define/set branch.subBranches[0] in the parent's controller function (opposed to link).

EDIT:

Here is a plunker of what I suspect is happening in your case (open the console when running):

http://plnkr.co/edit/YMLAtGc38oc3vVqebH3Z?p=preview

Here is a plunker of the suggested fix:

http://plnkr.co/edit/EjrSorCvFLcDkODe2Anm?p=preview