Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

Lately I've been building some modules and in some of them I only used controllers (controller is set within an existing directive I already need to use to load template) to have this comunnication between services and the view, for example:

$scope.callFunction = function(data) {
    factRequest = saveData(data);
};

I also noticed I could do this from within a directive, like this:

link:function(scope) {
    scope.callFunction = function(data) {
        factRequest.saveData(data);
    }
}

//or..

link:function(scope, element, attr) {
    attrValue = attr.myValue;
    element.bind('click', function(attrValue) {
        factRequest.saveData(attrValue);
    });
}

//or even..

link:function(scope, element, attr) {
    attrValue = attr.myValue;
    element.bind('click', function(attrValue) {
        factRequest.saveData(attrValue);
    });

    var elButton = element.fin('span'); //for example
    elButton.bind('click', function(attrValue) {
        factRequest.saveData(attrValue);
    });
}

Considering a scenario where this a reusable object, for example, a product where it display on multiple pages and have a commom function, such as addFavorite, addCart, addWishList, etc.. And also considering performance.

What is the difference between those call methods? And what is the best option to use as a call Function?

share|improve this question
up vote 1 down vote accepted

To restate, you are calling a service method on a click event and want to know where the best place to put that logic is.

Let's look at each of your examples:

Controller

angular.module('myApp').controller('MyController', function($scope, factRequest) {
  $scope.callFunction = function(data) {
    factRequest.saveData(data);
  };
});

First of all, whenever I find myself injecting $scope into a controller I question my approach. This is because adding variables to the current scope creates hidden dependencies if you are relying using those variables in a child controller -- and is unnecessary if you are not.

Instead, you should be using the controllerAs syntax and adding the function to the controller itself. Something like this:

angular.module('myApp').controller('MyController', function(factRequest) {
  var vm = this;
  vm.callFunction = function(data) {
    factRequest.saveData(data);
  };
});

...and you would access it in your template like this:

<div ng-controller="MyController as vm">
  <input ng-model="vm.data">
  <button ng-click="vm.callFunction(vm.data)">
    Click Me!
  </button>
</div>

This is a perfectly good approach utilizing native Angular directives.

Directive w/ Link Function

angular.module('myApp').directive('myDirective', function(factRequest) {
  return {
    link: function(scope) {
      scope.callFunction = function(data) {
        factRequest.saveData(data);
      }
    }
  };
});

Again, I don't like this because you are adding the function to scope. If you have a directive and want to expose some functionality to the template, you should use a controller. For example:

angular.module('myApp').directive('myDirective', function() {
  return {
    controller: 'MyDirectiveController',
    controllerAs: 'myDir',
    template: '<input ng-model="myDir.data">' +
      '<button ng-click="myDir.callFunction(myDir.data)">' +
      'Click Me!' +
      '</button>'
  };
}).controller('MyDirectiveController', function(factRequest) {
  var myDir = this;
  myDir.callFunction = function(data) {
    factRequest.saveData(data);
  }
});

This is essentially the same as the first example, except that it is now a reusable component.

Directive w/ Click Event Handler

angular.module('myApp').directive('myDirective', function(factRequest) {
  return {
    link: function(scope, element, attr) {
      element.on('click', function() {
        factRequest.saveData(scope.$eval(attr.myValue));
      });
    }
  };
});

Notice I took a few liberties here. For one thing, an event handler function gets the event object as its first argument, so trying to pass attr.myValue wouldn't work. Also, I call scope.$eval(), which is a best practice that enables the use of Angular expressions in the myValue attribute.

I like this approach best, because it doesn't rely on the use of other directives like ng-click. In other words, this directive is more self-contained.

One thing I should add is that Angular will not remove this event listener when the element is removed from the DOM. It is a best practice to clean up after your directive, like this:

angular.module('myApp').directive('myDirective', function(factRequest) {
  return {
    link: function(scope, element, attr) {
      var removeEventListener = element.on('click', function() {
        factRequest.saveData(scope.$eval(attr.myValue));
      });
      scope.$on('$destroy', removeEventListener);
    }
  };
});

Conclusion

From a performance perspective, all of these approaches are roughly equivalent. The first two don't add any watchers themselves, but ng-click and ng-model do so its six of one, half a dozen of the other.

share|improve this answer
    
Excellent explanation! I'm already migrating to use only controllerAs syntax, but all of the rest, It cleared my mind. I had this doubt because of the high use of $scope = function and also the high number of topics about avoiding $scope and since this needs to be very reusable, I tought using diretive link functions had better performance. But either link or controllerAs functions will give me the same result. Is it correct? – CelsomTrindade Jan 30 at 20:58
1  
Yes, the main difference between the link function and controller is that the link function should be used for DOM manipulation, observing attributes, and adding event handlers (among other things; see transclude and require options for directives)...while the controller should be used to expose data and functionality to the template (and other directives that might require your directive). So the controller is your directive's public API. – Shaun Scovil Jan 30 at 21:10

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.