AngularJS: Calling a directive function from a controller the right way - angularjs

I have two scenarios:
A function defined in a controller is called from a directive (see plunk).
The directive includes a '&' scope restriction to relate the controller and directive functions. If you click on the text, the element click event is triggered in the directive and the controller function is called. $scope.$apply() is used to notify AngularJS of the click event and refresh the value of var on the screen.
A function defined in a directive is called from a controller (see plunk).
The directive doesn't have any scope restriction, meaning that $scope in the controller and scope in the directive are shared. I defined func1() in the directive that can be called from the controller (try clicking on the text), however it seems intrusive as the controller needs to know the name of the function.
Is there a way to define func1() in such a way that the function name is declared in the directive div, similar to scenario 1?

Both options are possible and acceptable depending on how you are building the component.
Option 1 - Controllers should register listeners/callbacks and set attributes on directives (when using isolated scope - which i recommend). Controllers should not call a directives function but instead should change an attribute that has been bound on the directive. The directive if setup correctly should be watching this attribute and update accordingly.
Directives should not know who their controllers are in my opinion, it promotes decoupled code. Instead, controllers should be setting callback functions that directives can invoke at the appropriate time (i.e. letting the controller know something was clicked, selected, deleted, swiped, etc...)
Option 2 - Is acceptable to me when you are building a web component that provides the structure and behavioral logic but does not define the actual individual components UI (not including a possible skeleton UI). For example a list directive. The individual list may not be defined in the directive but "plugged in" for each context or use, giving us a more modular and reusable component. It does require us to know some things about the directive. It also requires that we modify the transclusion function to use the directives scope on the transcluded content instead of the original controllers scope. For an example you can checkout a list component I made as an example to this point.
http://github.com/Spidy88/ng-web-components
A snippet of text from this html page. The sf-list directive is an isolated scope directive that defines a lists behavior. However we can still define what each individual list item looks like with modified transclusion. It relies on us to call selectItem though in order to trigger selection behavior on the list.
<sf-list items="ctrl.emails" listener="ctrl.adapter">
<div class="list-item email" ng-click="selectItem(item)" ng-repeat="item in items track by item.id">
<div class="from">{{ item.from }}:</div>
<div class="subject">{{ item.subject }}</div>
</div>
</sf-list>

Related

How to chain nested directives which use isolated scopes in angularjs

I'm trying to chain two nested directives that both use isolated scopes.
<div ng-controller="myController">
<my-dir on-done="done()">
<my-dir2 on-done="done()">
</my-dir2>
</my-dir>
</div>
I would like the second directive (my-dir2) to call the done() function of the first directive (my-dir) which in turn would call the controller one.
Unfortunately I don't know how to make the second directive access the callback of the first directive (so far the second directive is looking inside the high level controller, bypassing the first directive).
I think one could possibly make use of "require" but I can't since the two directives are not related (I want to use my-dir2 inside other directives not only my-dir).
To make it clear : I don't want to use require because it means that there would be a dependency of myDir on myDir2. My point is : I want to be able to reuse myDir2 inside others directives. So I don't want myDir2 to be based on myDir but I do want to inform the upper directive (myDir) when something is done (like in a callback in js).
I have made a plunker : as you can see in the javascript console, my-dir2 is calling directly the done function from the high level controller.
Does anyone has a clean way to deal with that kind of situation ?
Thanks
Update:
to be able write directives that are independent of each other you need to use events:
use $emit('myEvent', 'myData') to fire an event that will be handled by scopes that are upward in the hierarchy.
use $broadcast('myEvent', 'myData') to fire an event that will be handled by scopes that are downward in the hierarchy.
to handle the event that was fired by $emit or $broadcast use $on('myEvent', function(event, data){\\your code})
P.S.: in your case the $emit won't work because both directives scopes are on the same level in the hierarchy so you will need to use $rootScope.$broadcast('myEvent' \*, myData*\); I've updated my plunker to reflect the needed changes http://plnkr.co/edit/eTkO6sk6hpuYPnCjlSKn?p=info
The following will make inner directive dependent on the outer directive:
basically to be able to call a function in the first directive you need to do some changes:
add require = '^myDir' to myDir2
remove the onDone from myDir2 and keep the isolated scope
scope:{}
add controller parameter to link function in myDir2 link:
function(scope,element,attrs,controller)
in myDir1 controller change the definition of the done function
from $scope.done to this.done
call controller.done() in myDir2
here is a plunker with the needed changes http://plnkr.co/edit/eTkO6sk6hpuYPnCjlSKn
I think you can do something like these:
angular.element('my-dir').controller('myDir').done();
give a try!

Angular: how to scope a function call to directive instead of controller?

<my-dir ng-show="isVisible()"></my-dir>
isVisible will call isVisible in the controller.
What if I want it to call isVisible inside the my-dir directive instead?
Note: my-dir in my application is a tree control that recursively calls itself using $compile so there may be many of them nested inside each other. Using a singleton service may not work because of asynchronicity.
EDIT: in retrospect I the correct answer was to create a filter for my directive. What can I say, Angular is a different way of doing things.
If you're using a directive you have full control of the element. Just do this: <my-dir check-visible="true"></my-dir>
Then in the directive's link function you can just go: if(attrs.checkVisible) isVisible();
Then you can show or hide the element however you like.

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

require one directive controller in another

These are the ways i tried to require one directive's controller in another one.
1) http://jsfiddle.net/Xarm2/1/
2) http://jsfiddle.net/82UKq/1/
In both the cases you can see in the firebug console, that the required directive's controller is either undefined or an empty object.
1) How can i access 'd1Cntrl' inside the d2 directive.
2)Please let me know one use case that will need me to require an directive controller in another directive.
See this nifty screencast from John Lindquist on directive to directive communication
For example, if you have an input element where you would like to apply two custom validations where one validation should only run if first validation passes (e.g. pattern matching validation should only activate if element has a value).
Few thinks i could like to put it here
1) Directives are not creating their own scope, For ex.
<superhero strength>The Hulk</superhero>
<superhero flight speed strength>Superman</superhero>
<superhero speed>The Flash</superhero>
these three directives will share the same scope, unless we explicitly say scope:true or scope:{} in the superhero definition. If you we are not creating new scope, then the last superhero will be in effect.
2) If we create a new scope at superhero level, that same scope is shared by the sibling directives like strength, flight etc..
3) If we require an directive controller inside another directive, the api that is defined on the directives controller using this keyword will be exposed to the requiring directive. Please note that the methods that are defined in the controller scope $scope wont be visible.
Please feel free to edit if the sentences making is wrong or the points were wrong.

Resources