In trying to get a grasp on creating my own AngularJS directives, I have an example that does everything I need, but realize that in borrowing from various examples, I now can create functionality for the directive's view in both the controller as well as the link.
It seems that I could get rid of the controller all together and just put everything into link, or is there something that I can do with the controller that I can't do with link?
http://jsfiddle.net/edwardtanguay/gxr49h96/6
.directive('itemMenu', function () {
var controller = function ($scope) {
var vm = this;
vm.addItem = function () {
$scope.add();
vm.items.push({
'kind': 'undefined',
'firstName': 'Joe',
'lastName': 'Newton',
'age': Math.floor(Math.random() * 60) + 20
});
};
// DOES THE SAME AS THE FUNCTION DEFINED BELOW IN LINK
// $scope.convertToInternal = function(item) {
// item.internalcode = 'X0000';
// item.kind = 'internal';
// };
};
return {
restrict: 'A',
scope: {
item: '=',
add: '&'
},
controller: controller,
controllerAs: 'vm',
bindToController: true,
template: '<div ng-include="getTemplateUrl()"></div>',
link: function (scope, element, attrs) {
scope.getTemplateUrl = function () {
switch (scope.item.kind) {
case 'external':
return 'itemMenuTemplateExternal';
case 'internal':
return 'itemMenuTemplateInternal';
default:
return 'itemMenuTemplateUndefined';
}
};
scope.convertToInternal = function(item) {
item.internalcode = 'X0000';
item.kind = 'internal';
};
},
};
})
You may find a lot of watery rants about controller vs link, most of them contain the information from $compile service documentation.
Answering the question directly,
controllers from other modules/files can be plugged into the directive via Angular DI with controller: 'Controller'
controller
can be injected with dependencies while link
has fixed arguments list of and gets by with directive's dependencies
controller
kicks in before link
, so it can prepare the scope for linking or recompile the element on some condition ASAP
controller function has this
, its code appearance complies to other OOP-like ES5 code, and the methods can be easily transferred between other code parts, e.g. service
service or third-party code
as the result, controllers are suited to be defined as ES2015 or TS classes.
directive's controller can be require
d by child directive and provides convenient one-way interaction between those two
controller
makes use of bindToController: true
+ controllerAs: 'vm'
and implements $scope.vm
recipe (particularly useful to fight JS prototypal inheritance) while keeping this
syntax
bindToController
object value provides attribute bindings for inherited scope: true
scope, no more $attr.$observe
bindToController
object value provides further granularity for isolated scope. If there are certain attributes that should be bound to the controller and accessed with require
, it can be done now
Which code goes to controller
and which to link
is more delicate question.
It is conventional to use Angular controllers as view models, as in MVVM (hence the controllerAs: 'vm'
convention), so if there is a job for controllers (i.e. binding the services to scope values or setting up scope watchers) give it to them, leave the rest to link
.
Since $attrs
, $element
and $transclude
local dependencies should be injected into controller explicitly, the one may consider it a sign to skip them ($scope
should be injected too for $scope.$
methods, but we will just ignore this fact).
There are some non-religious concerns about the job that should be done by link
and not by controller
. require
d controllers aren't available in controller itself, so this kind of directive interaction takes place in link
. Since controller
launches at earlier compilation phase than link
, bound attribute values won't be interpolated yet, so the code that depends on these scope values goes to link
. The same applies to other DOM-related code, it goes to link
for a reason.
It is mostly the matter of proper code style and not real necessity. As of the code in the example, all scope
stuff is controller-friendly, I don't think that link
should be there at all.