15

I have a directive which interacts with Box file picker. My directive is used by 2 separate controllers, with the possibility of adding more in the future.

Box file picker lets you set a callback function once the user selects a file/folder, like this:

var boxSelect = new BoxSelect();
// Register a success callback handler
boxSelect.success(function(response) {
    console.log(response);
});

My controllers are using the directive and they have the success callback logic as scope variables, which I'm passing to the directive.

I created a plunkr where I'm mocking the Box select behavior

Controller

.controller('myController', function($scope) {
  $scope.onSuccessful = function(message) {
    alert('Success! Message: ' + message);
  };
})

Directive

angular.module('myApp', [])
  .controller('myController', function($scope) {
    $scope.onSuccessful = function(message) {
      //message is undefined here
      alert('Success! Message: ' + message);
    };
  })
  .directive('myDirective', function() {
    return {
      restrict: 'A',
      scope: {
        success: '&'
      },
      link: function(scope, element) {

        //third party allows to subscribe to success and failure functions
        function ThirdPartySelect() {

        }

        ThirdPartySelect.prototype.success = function(callback) {
          this.callback = callback;

        };

        ThirdPartySelect.prototype.fireSuccess = function() {
          this.callback({
            foo: 'bar'
          });
        };

        var myThirdPartyInstance = new ThirdPartySelect();
        myThirdPartyInstance.success(function(message) {
          //message is still defined here, but not in the controller
          scope.success(message);
        });

        element.on('click', function() {
          myThirdPartyInstance.fireSuccess();
        });

      }
    };
  });

View

<div ng-controller="myController">
  <button my-directive success="onSuccessful(arg)">Test</button>
</div>

The callback function gets called inside the controller but the arguments are undefined.

I was able to fix this by using '=' instead of '&', but I'd like to know why it wasn't working with '&' since it is supposed to be used for method binding

Community
  • 1
  • 1
yvesmancera
  • 2,915
  • 5
  • 24
  • 33

1 Answers1

20

Yes, to bind a controller function to your directive, you have to use the & bindings (expression binding) which allows the directive to call an expression or a function defined by a DOM attribute.

But in your directive, when you call your binded method, the function parameter should be an object where the key are the same parameter that you declare in your controller, when you define your function.

So in your directive, you have to replace :

scope.success(message);

by :

scope.success({message:message.foo});

Then in your HTML, you have to replace :

 <button my-directive success="onSuccessful(arg)">Test</button>

by :

<button my-directive success="onSuccessful(message)">Test</button>

You can see the Working Plunker

Paul Boutes
  • 3,285
  • 2
  • 19
  • 22
  • The argument name should coincide with the one in expression in html. In controller there is a formal parameter and it can have whatever name. So there is no need to rename param in html, just need to provide `arg` in the object when sending from directive. `scope.success({arg:message.foo});` – Kirill Slatin Aug 13 '15 at 23:40
  • 2
    It worked, thanks a lot! I couldn't find this documented in the official docs, this seems kind of counter-intuitive, why wouldn't they just let me pass a real reference to the function instead of a weird proxy function? – yvesmancera Aug 14 '15 at 17:05