2

I have a scenario where a directive's link function is called with an empty element parameter leading to errors.

A code example describes the scenario best:

index.html:

<html ng-app="app">
<body>
    <div outer></div>
</body>
</html>

Scripts:

var app = angular.module('app', []);

app.directive('outer', function() {
    return {
        replace: true,
        controller: function($scope) {
            $scope.show = true;
        },
        templateUrl: 'partial.html'
    };
});

app.directive('inner', function($window) {
    return {
        link: function(scope, element, attrs) {
            console.log('element: ' + element);
            var top = element[0].offsetTop;
        }
    };
});

partial.html (referenced by templateUrl on outer, above):

<div ng-switch on="show">
    <div ng-switch-when="true">
       <div inner>showing it</div>
    </div>
</div>

Loading index.html in Chrome, the console reports the error: "TypeError: Cannot read property 'offsetTop' of undefined" - because element is an empty array!

Some notes:

  • replace must be set to true on directive outer.
  • templateUrl must be used by directive outer to load its partial.

I'm uncertain whether I've overlooked some configuration requirement or if this is an Angular issue. Is this a valid scenario? If it is then is it a problem with ng-switch or is there a deeper problem within Angular?

mb21
  • 34,845
  • 8
  • 116
  • 142
Paul Pepper
  • 707
  • 1
  • 8
  • 17
  • 1
    ng-switch seems to call directive twice.. first time element doesn't exist in directive function, second time it does ( likely the compile phase of ng-switch). May not be best solution but `if(element.length)` seems to work http://plnkr.co/edit/sxfHOarFr5OcFMIt3dRj – charlietfl Apr 03 '13 at 13:12
  • My understanding is that the compile phase doesn't call a directive's `link` function - is this correct? Re. the guard, see my response to @arun-p-johny, below. – Paul Pepper Apr 03 '13 at 14:29
  • not 100% sure why...but look at console logging in demo. If running third party code like jQuery...it will fail silently if `element.somePlugin()` is empty – charlietfl Apr 03 '13 at 15:40
  • That Angular invokes `outer''s `link` function twice seems odd - the 1st without a valid `element` param, the 2nd with a valid `element` param. Omitting the ng-switch gives the expected result of a single call to `link` (see http://plnkr.co/fV2yq1wy8jignNmfgp8Y). – Paul Pepper Apr 03 '13 at 21:06

1 Answers1

0

Try

app.directive('inner', function($window) {
    return {
        link: function(scope, element, attrs) {
            console.log('element: ',  element);
            if(!element.length){
              console.log('Returning since there is no element');
              return;
            }
           var top = element[0].offsetTop;
           console.log('top', top)
        }
    };
});
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • 2
    Yes, checking element would work, except directive `inner` may be a 3rd-party component. In fact I ran into this issue using an [angular-ui](http://angular-ui.github.com/) component. I'm trying to understand what angular is doing (possibly in error) rather than ways to effectively guard against this issue. – Paul Pepper Apr 03 '13 at 14:30