Angular.js

Learning Angular: Verifying whether a function has been passed to my directive’s isolated scope

When you create isolated directives, you usually use the scope property to define your directive’s external API. Some of the APIs properties might be mandatory while others are optional and based on that state, your directive might react differently. Sounds easy, but I stumbled upon a difference when you pass in functions vs. data objects. See yourself.

This article is part of my “Learning NG” series, presenting some of my adventures while learning Angular. Check out the series intro and other articles. Note, I’m an Angular newbie, so I’m more than happy for any kind of feedback and improvement suggestions from more experienced people than me.

Problem

So I have some properties defined on my isolated directives using the scope property:

...
scope: {
    data: '=',
    clickFn: '&'
}

Not all of these properties may be required, so there can be the necessity for checking whether the user provided the directive’s property and hence react differently. With plain data properties, such as like data: '=' in the example before, it is quite easy. Simply use the ng-show (or ng-hide) directive in your template:

...
scope: {
    data: '='
},
template: '<div ng-show='data'>Some content</div>',
...

The div won’t be shown if data is not defined. Why? Well, if data is not provided in the directive, then the $scope.data is undefined within your directive’s scope, thus evaluating to false and hence hiding the div.

Strangely, this is not the case when passing functions into a directive’s scope using the & notation. In that case, regardless of whether you provide a function on your directive or not, $scope.clickFn will always contain this:

function (locals) {
  return parentGet(parentScope, locals);
}

You can verify this by yourself. Simply implement the directive’s link function, debug it and inspect the $scope property.

Solution

The solution here is to inspect the iAttr parameter of your directive’s link function rather than the $scope object. Inspect it and then assign a variable to the scope which you can then use in your template. Something like this:

angular.module('someModule', [])
    .directive('myDirective', function() {
        return {
          restrict: 'E',
          scope: {
            clickFn: '&'
          },
          template: [
            '<div ng-show='isFn' style='border: 1px solid; padding:5px'>',
                '<a href='#' ng-click='clickFn()'>Click me</a>',
            '</div>'
            ].join(''),
          link: function($scope, iElem, iAttr){
            // use the iAttr to check whether the property is defined
            $scope.isFn = angular.isUndefined(iAttr.clickFn) === false;
          }
        };
      });

Background

Each function that is passed into the isolated scope of a directive is wrapped like this:

 directive-scope-fn-isolateBindingContext

Here’s the source of parentGet:

directive-scope-fn-parentGet

From a bird’s perspective, it seems that it is responsible for safely invoking the passed function with the local scope of the directive. But as said, I didn’t go into the details.

Juri Strumpflohner

Juri Strumpflohner mainly operates in the web sector developing rich applications with HTML5 and JavaScript. Beside having a Java background and developing Android applications he currently works as a software architect in the e-government sector. When he’s not coding or blogging about his newest discoveries, he is practicing Yoseikan Budo where he owns a 2nd DAN.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button