1

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?

1 Answer 1

2

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) {
      function onClick() {
        factRequest.saveData(scope.$eval(attr.myValue));
      }
      element.on('click', onClick);
      scope.$on('$destroy', function() {
        element.off('click', onClick);
      });
    }
  };
});

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.

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

2 Comments

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?
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.

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.