42

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.

1
  • Check the attrs array in the directive's link function Commented Feb 21, 2014 at 13:01

2 Answers 2

91

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.

Sign up to request clarification or add additional context in comments.

4 Comments

This worked perfectly and is a much more elegant solution. Should be the correct answer.
There is not a single word about it in the docs.
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.
Docs: docs.angularjs.org/api/ng/service/$compile Directive Definition Object -> scope section
44

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

2 Comments

helped me, but instead of angular.isDefined I prefer vanilla-js attrs.hasOwnProperty('callback');
@Stalinko: if you want to use vanila-js for some reason, a simple check for undefined should work: if (typeof attrs.callback === "undefined")

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.