AngularJS - Directives - What's the reason behind the following syntax? - angularjs

My question is related to directories and two of its properties.
What is the reason that the parameters for the link function don't have a $ sign as a prefix?
e.g
link: function (scope, element, attrs, ctrl) {
scope.$watch("item.quantity", function () {
ctrl.updateTotal();
});
}
in contrast to the controller property:
controller: function ($scope, $element, $attrs) {
}
I know that the first one is a link function and thus is written without a $ sign.
But why make a difference? Is it partly because you can create your own scope in a directive and as a result the scope doesn't necessarily mean to be related with the $scope of the controller?
That would explain the scope parameter but I can't think of any explanation regarding the other ones.
Thanks in advance.

By convention the $ prefixes are not used with functions which are not injected by the $injector. So the link: function (scope, element, attrs [,ctrl]) will not be injected. It always has the same parameters in the same order.
When dealing with functions where the dependency injection provides the arguments, you must use $scope, otherwise it won't be injected.
TL;DR
This is more or less to confuse Angular learners, but it is vital that you have to use positioned parameters for some functions (like postLink).
Additional info and links
For those of you who want to know the exact details, I recommend reading following chapters of AngularJS Guide:
Creating Directives that Communicate, please read the paragraph above the Summary.
Comprehensive Directive API
In the latter link you can see the directive definition object containing some methods with positioned parameters (please note that in this example there are many options which don't make sense in combination):
myModule.directive('directiveName', function factory(injectables) {
var directiveDefinitionObject = {
template: function(tElement, tAttrs) { ... },
templateUrl: function(tElement, tAttrs) { ... },
// controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
},
link: {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
// or
link: function postLink(scope, iElement, iAttrs, controller) { ... }
};
return directiveDefinitionObject;
});
If you use a controller function in the DDO (abreviation for directive definition object), the arguments will be injected (hence the prefix $):
// ... somewhere in the DDO ...
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
You can find documentation for the "special" injectables here
Sure there are more than the mentioned functions with positioned parameters, e.g. the $animate enter(), leave() etc.

Angular is just calling a method with these specific objects as parameters. If we wanted to (but of course it goes against convention), we could name these parameters whatever we wanted to, as long as we retained the order they were being used.

Related

Which function will execute first in angularjs directives? link or controller?

I have gone through this link https://www.undefinednull.com/2014/07/07/practical-guide-to-prelink-postlink-and-controller-methods-of-angular-directives/, they said the order(first to last) of execution of link and controller is
Controller,
Pre-Link function,
Post-Link function
But here I read AngularJS: What is the need of the directive's link function when we already had directive's controller with scope? link executes before the controller. Which one I should believe in?
If it was first link then controller it wouldn't be possible to require other directives and and use theirs controllers in link function.
Take a look at the code from documentation:
var directiveDefinitionObject = {
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
controllerAs: 'stringIdentifier',
require: 'siblingDirectiveName', // requiring another directive
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... }, //siblingDirectiveName's controller is available in link function
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
},
};
return directiveDefinitionObject;
});
To support this statement we can read o the same page :
controller
Controller constructor function. The controller is instantiated before the pre-linking phase and can be accessed by other directives (see require attribute). This allows the directives to communicate with each other and augment each other's behavior.

Angular directive manual compile in link function + transclude

I'm in need of doing transclusion together with manual compilation in link function and I'm wondering if there is a better way than what I'm pasting below as "Dynamic". It seems to be working but I'm not sure what are the consequences of removing ng-transclude attribute and I'm removing it due to Angular complaining to Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <div ng-transclude="">.
Bottom-line is that I need to provide different templates for different instances of this directive with fallback to generic ones.
I can't use compile part. Here I have just simple example but I have also other cases with big link function that I would rather don't want to convert as this should be just "golden bullet" type of adjustment.
This little guy can be found here https://github.com/institut-de-genomique/Ultimate-DataTable/blob/master/src/directives/udt-form.js in original form.
Original
angular.module('ultimateDataTableServices')
.directive('udtForm', function () {
return {
restrict: 'A',
replace:true,
transclude:true,
templateUrl:'udt-form.html',
link: function(scope, element, attr) {
}
};
});
Dynamic
angular.module('ultimateDataTableServices')
.directive('udtForm', ['$compile', '$templateCache', function ($compile, $templateCache) {
return {
restrict: 'A',
transclude:true,
link: function(scope, element, attr, ctrls, transclude) {
var tplName = 'udt-form.html',
tplPath = scope.udtTable.configMaster.templates[tplName] || scope.udtTable.configMaster.templatesMaster[tplName] || tplName,
elem = angular.element($templateCache.get(tplPath));
elem.find('[ng-transclude]').removeAttr('ng-transclude').append(transclude());
element.replaceWith($compile($templateCache.get(tplPath))(scope));
}
};
}]);
TIA!

adding angular dependencies in a link function shared across multiple directives

I have several directives that use the same link function. (The link function adds some extra state and html depending on the use case.) So I declared this as follows:
function common_linkfunc(){...}
var Directive_1 = function($scope, etc) {
return {restrict:"E", ...
link: common_linkfunc,
controller: function($scope, etc) {...}
};
}
Directive_1.$injects = ["$scope", "etc"];
angular.module("module").directive("D1", Directive_1)...;
First change was when link function required $compile. Next I need to add $templateCache and my question is how can I do this systematically?
My first approach was to rewrite common_linkfunc as
function foo($compile, $templateCache) {
return common_linkfunc($compile, $templateCache) {...}
}
and then use this in every directive:
...
link: foo($compile, $templateCache),
...
But this is copy-and-paste! Is there an easier and less error prone way to do the same?
Regardless of the solution, you'll need to pass some argument to your common link function, because Angular won't inject anything into it for you. That being said, I can think of two different approaches:
1) Use arguments
app.directive('foo', function($http, $timeout) {
return {
restrict: 'E',
link: linkFn1.apply(null, arguments)
}
});
function linkFn1($http, $timeout) {
return function(scope, element, attrs) {
// ...
};
}
The downside here is that the order of the arguments in the directive function matters. If some other directive uses a different order, the code won't work properly.
2) Use $injector
app.directive('bar', function($injector) {
return {
restrict: 'E',
link: linkFn2($injector)
}
});
function linkFn2($injector) {
var $http = $injector.get('$http'),
$timeout = $injector.get('$timeout');
return function(scope, element, attrs) {
// ...
};
}
Working Plunker

Are the link functions differernt in directive compile returning and link function?

All:
[UPDATE]: Got answer from this post(which takes time to read, so I just quote a simple answer of my understanding to that post here):
"Note that if you need a compile function and a link function (or pre
and post link functions), the compile function must return the link
function(s) because the 'link' attribute is ignored if the 'compile'
attribute is defined."
I am pretty new to AngularJS directive, when I learn how to use $compile, I got one question, for example:
I can define a directive like:
app.directive("customdir", function(){
return {
restrict:"AE",
compile: function(tmplEL, attrs){
return function(scope, EL, attrs){/*this is the link function*/};
},
link: function(scope, EL, attrs){/*this is also the link function*/}
}
})
My confusion here is: the compile function return a link function while we can define another link function in directive as well, are they same function or they are different function that the link: function(scope, EL, attrs) can override the link function returned from compile?
And what is the result if I defined both?
Thanks
It's an "either/or".
You can define a compile property on the directive declaration object, which is a function that returns a link function/object - then the link property is ignored.
compile: function(tElement, tAttrs, transclude){
return {
pre: function prelink(){...},
post: function postlink(){...}
};
}
Or, since compile function is less commonly used, you could just specify the link property - which could be the post-link function (if it's a function value) or both pre-link and post-link, if it's an object value with these fields:
link: {
pre: function prelink(scope, element, attrs, ctrls, transclude){
},
post: function postlink(scope, element, attrs, ctrls, transclude){
}
}
Or simply:
link: function postlink(scope, element, attrs, ctrls, transclude){
}
More info in Angular docs

How to override image source within a directive

I want to override an image src within a directive. I've tried the following, none of which work.
app.directive('imgTransform', function () {
return {
retrict: 'A',
link: function (scope, elem, attrs) {
elem.attr('ng-src', 'foo');
attrs.ngSrc = 'foo';
elem.attr('src', 'foo');
attrs.src = 'foo';
}
};
});
Does the link function execute before the DOM binding takes place?
I also tried creating an isolate scope and binding to the ngSrc attribute:
scope: {
src: '#ngSrc'
},
Then setting scope.ngSrc in the link function. This did not work either.
Am I missing something?
http://jsfiddle.net/benfosterdev/y7JE9/
You need to modify it before you get to your linking function.
I'd suggest in the controller function, as opposed to digging to deep into the $compiler.
restrict: "A",
controller: function ($scope, $element, $attrs) {
$attrs.$set('ngSrc', 'someOtherValue');
},
link: function (scope, el, attrs) {...}
Here's a fiddle: http://jsfiddle.net/y7JE9/5/
Also, use $observe when looking up interpolated values in your link function. Good luck : )

Resources