How to check if a method argument of a directive is specified in AngularJS?

14,194

Solution 1

Using '&?' returns undefined if the attribute has not been set.

'&' = callback function is defined always.

'&?' = callback function is defined only when attribute is defined in html template.

bindToController: {
    callback: '&?'
},
controller: function() {
    if (this.callback === undefined) {
        // attribute "callback" was not defined
    }
}

Note: Works in Angular 1.4.8. I'm not sure if it works in older versions.

Solution 2

Looking at angularjs source code, I see this:

case '&':
    parentGet = $parse(attrs[attrName]);
    isolateScope[scopeName] = function(locals) {
         return parentGet(scope, locals);
    };
    break;

The parentGet is the bound function expression. Unfortunately, this is a local variable which is only available to the function assigned to isolateScope[scopeName] via closure.

Instead of trying to find a way to get that variable, a simple solution is just to check the attrs. Try:

link: function(scope,elem,attrs) {

      scope.hasCallback = function() {
        return angular.isDefined(attrs.callback);
      }
    }

DEMO

Share:
14,194
Ferenc T
Author by

Ferenc T

Updated on August 08, 2022

Comments

  • Ferenc T
    Ferenc T almost 2 years

    I've created a custom directive which contains a button. This button calls a method from parent scope specified by 'callback' attribute.

    <!DOCTYPE html>
    <html ng-app="app">
    <head>
        <title>Simple directive</title>
    
        <script src="js/lib/angular/angular.js"></script>
    
        <script type="text/javascript">
            var app = angular.module('app', []);
    
            app.controller('TestController', function($scope) {
    
                $scope.doSomething = function(param) {
                    alert('Something called with: ' + param);
                }
            })
    
            app.directive('myDirective', function() {
                var ret = {
                    restrict: 'E',
                    scope: {
                        user: '@',
                        callback: '&'       // bound a function from the scope
                    },
                    template: '<div>Hello {{user}}<button ng-show="hasCallback()" ng-click="callback({userData: user})">Callback</button>',
                    controller: function($scope) {
                        $scope.hasCallback2 = function() {
                            var t = typeof $scope.callback;
                            return t == 'function';
                        }
    
                        $scope.hasCallback = function() {
                            return angular.isDefined($scope.callback);
                        }
                    }
                };
                return ret;
            });
    
        </script>
    </head>
    
    <body ng-controller="TestController">
    
    <my-directive user="cat" callback="doSomething(userData)"></my-directive>
    <my-directive user="dog" callback="doSomething(userData)"></my-directive>
    <my-directive user="pig"></my-directive>
    
    </body>
    
    </html>
    

    My question is:

    How can I control visibility of button inside template? I'd like to hide it if callback attribute not specified in custom tag (see 3rd my-directive tag). When I check typeof of callback, I always get 'function' and angular.isDefined(...) also returns true.

  • Rebecca
    Rebecca about 8 years
    This worked perfectly and is a much more elegant solution. Should be the correct answer.
  • fracz
    fracz almost 8 years
    There is not a single word about it in the docs.
  • noherczeg
    noherczeg almost 8 years
    This does not work in 1.3.18, it always wires in a wrapper function no matter if it is defined in the DOM or not.
  • vip
    vip about 7 years
    Docs: docs.angularjs.org/api/ng/service/$compile Directive Definition Object -> scope section
  • Stalinko
    Stalinko about 7 years
    helped me, but instead of angular.isDefined I prefer vanilla-js attrs.hasOwnProperty('callback');
  • Khanh TO
    Khanh TO about 7 years
    @Stalinko: if you want to use vanila-js for some reason, a simple check for undefined should work: if (typeof attrs.callback === "undefined")