Angular directive, link not called when attribute updates - angularjs

In the following example: http://plnkr.co/edit/OZjg6sUgl35GIriaabQg?p=preview
I've got 2 directives, the showCard one inside an ng-repeat which link function get called any time the attribute is updated. (see the console)
The other one showCards is working properly but the link function is not called when the attribute is updated, but only once at the beginning.
I'd like to understand the difference between those 2 kind.

The linking function is only invoked once per element, so whenever you add a new card the ngRepeat-directive will add a new <show-card ...> which will invoke the link function.
If you want some function to trigger every time cards is changed you can add a $watch function on the scope in the showCards link function, like this:
$scope.$watch('cards',function(){
console.log('multi',$scope.cards);
},true);

Related

AngularJS using the $compile service to add angular elements dynamically, added directives post link functions are executed incorrectly

Here is a link to how directive link function executed in the correct order(not my article):
Correct Execution Order
Basically if you have nested directives like so:
<level-one>
<level-two>
<level-three>
</level-three>
</level-two>
</level-one>
The execution order should look like this:
COMPILE PHASE
levelOne: compile function is called
levelTwo: compile function is called
levelThree: compile function is called
PRE-LINK PHASE
levelOne: pre link function is called
levelTwo: pre link function is called
levelThree: pre link function is called
POST-LINK PHASE
levelThree: post link function is called
levelTwo: post link function is called
levelOne: post link function is called
When using the $compile service to add directive dynamically to a view the post link functions are executed in reverse order. Where the parent directive's post link function is called before its children's post link function are called.
Here's a link to my plunker that demonstrates this: Plunker
You will notice in the console that the "label" directive post link function is being called after its "parent" directive is called.
NOTE: I had to add an extra "parent" directive to the DOM or it would fail and you would not be able to see the execution order.
Is this by design or this a bug in angular?

Directive link function called multiple time in ngRepeat

i have a strange behavior in my code with custom directive.
The link function is called multiple times but as example if want to initialize a third-part library (example maps) this behavior breaks the lib.
link: function(scope) {
alert("invoked");
}
http://jsfiddle.net/dYs8L/1/
jsfiddle updated with the correct code
http://jsfiddle.net/dYs8L/2/
It's working as designed...ng-repeat essentially creates a new copy the markup with an isolated scope for every item in the collection. If you need to initialize something once, you'll need to do it elsewhere. You can consider putting this resource into a service; that will allow all the elements output by ng-repeat to use the same instance.

Angular directive link function called twice

In my angular app, directives are working fine during the first visit, but once a page been visited twice, all the directive link function gets called twice too. Say I am on page A, click a link to go to page B and then back to page A, all directives on page A will execute the their link function twice. if I refresh the browser it will become normal again.
Here is an example where the console.log will output twice when the second visit.
#app.directive 'testChart', ["SalesOrder", (SalesOrder) ->
return {
scope: {options: '='}
link: (scope, elem, attrs) ->
console.log("............checking")
SalesOrder.chart_data (data) ->
Morris.Line
element: "dash-sales"
data: data
xkey: 'purchased_at'
ykeys: ['total']
labels: ['Series a']
}
]
Any idea?
Update
My Route
when("/dash", {
templateUrl: "<%= asset_path('app/views/pages/dash.html') %>",
controller: DashCtrl
}).
so my chart is duplicated
also make sure you are not including your directive in your index.html TWICE!
I had this exact same problem.
After a loooooong time digging around I found that I hadn't used the correct closing tag which resulted in the chart being called twice.
I had
<line-chart><line-chart>
instead of
<line-chart></line-chart>
The link() function is called every time the element is to be bound to data in the $scope object.
Please check if you are fetching data multiple times , via GET call. You can monitor the resource fetching via Network tab , of chrome debugger.
A directive configures an HTML element and then updates that HTML subsequently whenever the $scope object changes.
A better name for the link() function would have been something like bind() or render(), which signals that this function is called whenever the directive needs to bind data to it, or to re-render it.
Maybe this will help somebody...
I had a problem with directive transclude, I used a transclude function which was adding child elements and also at the same time I forgot ng-transclude in directive template. Child elements were also directives and their link function was called twice!
Spent some time on this one..
More in details:
I had a "main" directive and "child" directives, idea was to use one inside another, something like that:
main
child
child
So problem was that link of "child" directive was called twice, and I didn't understand why,
Turned out I had ng-transclude in "main" directive template (I am posting it as it is in PUG format, sorry for that):
md-card(layout-fill)
md-card-content(flex)
.map-base(id="{{::config.id}}", layout-fill)
ng-transclude
and also in link function of "main" directive I called transclude function:
link: function($scope, $element, $attrs, controller, transcludeFn) {
$element.append(transcludeFn());
}
I think I just tried different combinations and forgot about that, visually everything was ok, but link was called twice and code was running twice and logic was broken..
So problem is that you can't have both and you have to choose one of the ways.
Hopefully now it is more clearer.
In my case I had a main-nav and sub-nav that both called a directive by its name attribute. Since the first instance already set the scope needed the second sub-nav the 2nd call wasn't needed. Incase anyone has a similar issue.

Angularjs - directive best practice function in link or controller block? [duplicate]

Some places seem to use the controller function for directive logic and others use link. The tabs example on the angular homepage uses controller for one and link for another directive. What is the difference between the two?
I'm going to expand your question a bit and also include the compile function.
compile function - use for template DOM manipulation (i.e., manipulation of tElement = template element), hence manipulations that apply to all DOM clones of the template associated with the directive. (If you also need a link function (or pre and post link functions), and you defined a compile function, the compile function must return the link function(s) because the 'link' attribute is ignored if the 'compile' attribute is defined.)
link function - normally use for registering listener callbacks (i.e., $watch expressions on the scope) as well as updating the DOM (i.e., manipulation of iElement = individual instance element). It is executed after the template has been cloned. E.g., inside an <li ng-repeat...>, the link function is executed after the <li> template (tElement) has been cloned (into an iElement) for that particular <li> element. A $watch allows a directive to be notified of scope property changes (a scope is associated with each instance), which allows the directive to render an updated instance value to the DOM.
controller function - must be used when another directive needs to interact with this directive. E.g., on the AngularJS home page, the pane directive needs to add itself to the scope maintained by the tabs directive, hence the tabs directive needs to define a controller method (think API) that the pane directive can access/call. For a more in-depth explanation of the tabs and pane directives, and why the tabs directive creates a function on its controller using this (rather than on $scope), please see 'this' vs $scope in AngularJS controllers.
In general, you can put methods, $watches, etc. into either the directive's controller or link function. The controller will run first, which sometimes matters (see this fiddle which logs when the ctrl and link functions run with two nested directives). As Josh mentioned in a comment, you may want to put scope-manipulation functions inside a controller just for consistency with the rest of the framework.
As complement to Mark's answer, the compile function does not have access to scope, but the link function does.
I really recommend this video; Writing Directives by Misko Hevery (the father of AngularJS), where he describes differences and some techniques. (Difference between compile function and link function at 14:41 mark in the video).
running code before Compilation : use controller
running code after Compilation : use Link
Angular convention : write business logic in controller and DOM manipulation in link.
Apart from this you can call one controller function from link function of another directive.For example you have 3 custom directives
<animal>
<panther>
<leopard></leopard>
</panther>
</animal>
and you want to access animal from inside of "leopard" directive.
http://egghead.io/lessons/angularjs-directive-communication will be helpful to know about inter-directive communication
compile function -
is called before the controller and link function.
In compile function, you have the original template DOM so you can make changes on original DOM before AngularJS creates an instance of it and before a scope is created
ng-repeat is perfect example - original syntax is template element, the repeated elements in HTML are instances
There can be multiple element instances and only one template element
Scope is not available yet
Compile function can return function and object
returning a (post-link) function - is equivalent to registering the linking function via the link property of the config object when the compile function is empty.
returning an object with function(s) registered via pre and post properties - allows you to control when a linking function should be called during the linking phase. See info about pre-linking and post-linking functions below.
syntax
function compile(tElement, tAttrs, transclude) { ... }
controller
called after the compile function
scope is available here
can be accessed by other directives (see require attribute)
pre - link
The link function is responsible for registering DOM listeners as well as updating the DOM. It is executed after the template has been cloned. This is where most of the directive logic will be put.
You can update the dom in the controller using angular.element but this is not recommended as the element is provided in the link function
Pre-link function is used to implement logic that runs when angular js has already compiled the child elements but before any of the child element's post link have been called
post-link
directive that only has link function, angular treats the function as a post link
post will be executed after compile, controller and pre-link funciton, so that's why this is considered the safest and default place to add your directive logic

The difference between a compile/link function and a controller function in AngularJS? [duplicate]

Some places seem to use the controller function for directive logic and others use link. The tabs example on the angular homepage uses controller for one and link for another directive. What is the difference between the two?
I'm going to expand your question a bit and also include the compile function.
compile function - use for template DOM manipulation (i.e., manipulation of tElement = template element), hence manipulations that apply to all DOM clones of the template associated with the directive. (If you also need a link function (or pre and post link functions), and you defined a compile function, the compile function must return the link function(s) because the 'link' attribute is ignored if the 'compile' attribute is defined.)
link function - normally use for registering listener callbacks (i.e., $watch expressions on the scope) as well as updating the DOM (i.e., manipulation of iElement = individual instance element). It is executed after the template has been cloned. E.g., inside an <li ng-repeat...>, the link function is executed after the <li> template (tElement) has been cloned (into an iElement) for that particular <li> element. A $watch allows a directive to be notified of scope property changes (a scope is associated with each instance), which allows the directive to render an updated instance value to the DOM.
controller function - must be used when another directive needs to interact with this directive. E.g., on the AngularJS home page, the pane directive needs to add itself to the scope maintained by the tabs directive, hence the tabs directive needs to define a controller method (think API) that the pane directive can access/call. For a more in-depth explanation of the tabs and pane directives, and why the tabs directive creates a function on its controller using this (rather than on $scope), please see 'this' vs $scope in AngularJS controllers.
In general, you can put methods, $watches, etc. into either the directive's controller or link function. The controller will run first, which sometimes matters (see this fiddle which logs when the ctrl and link functions run with two nested directives). As Josh mentioned in a comment, you may want to put scope-manipulation functions inside a controller just for consistency with the rest of the framework.
As complement to Mark's answer, the compile function does not have access to scope, but the link function does.
I really recommend this video; Writing Directives by Misko Hevery (the father of AngularJS), where he describes differences and some techniques. (Difference between compile function and link function at 14:41 mark in the video).
running code before Compilation : use controller
running code after Compilation : use Link
Angular convention : write business logic in controller and DOM manipulation in link.
Apart from this you can call one controller function from link function of another directive.For example you have 3 custom directives
<animal>
<panther>
<leopard></leopard>
</panther>
</animal>
and you want to access animal from inside of "leopard" directive.
http://egghead.io/lessons/angularjs-directive-communication will be helpful to know about inter-directive communication
compile function -
is called before the controller and link function.
In compile function, you have the original template DOM so you can make changes on original DOM before AngularJS creates an instance of it and before a scope is created
ng-repeat is perfect example - original syntax is template element, the repeated elements in HTML are instances
There can be multiple element instances and only one template element
Scope is not available yet
Compile function can return function and object
returning a (post-link) function - is equivalent to registering the linking function via the link property of the config object when the compile function is empty.
returning an object with function(s) registered via pre and post properties - allows you to control when a linking function should be called during the linking phase. See info about pre-linking and post-linking functions below.
syntax
function compile(tElement, tAttrs, transclude) { ... }
controller
called after the compile function
scope is available here
can be accessed by other directives (see require attribute)
pre - link
The link function is responsible for registering DOM listeners as well as updating the DOM. It is executed after the template has been cloned. This is where most of the directive logic will be put.
You can update the dom in the controller using angular.element but this is not recommended as the element is provided in the link function
Pre-link function is used to implement logic that runs when angular js has already compiled the child elements but before any of the child element's post link have been called
post-link
directive that only has link function, angular treats the function as a post link
post will be executed after compile, controller and pre-link funciton, so that's why this is considered the safest and default place to add your directive logic

Resources