According to the Angular docs:
Scope is the glue between application controller and the view. During the template linking phase the directives set up $watch expressions on the scope. The $watch allows the directives to be notified of property changes, which allows the directive to render the updated value to the DOM.
Now my Question is : if my function is not connected to the view, should we use $scope or not?
I assume that you mean if you should do $scope.functionName = function(), even if the function isn't connected to the view.
No you shouldn't, why would you expose a function to the view, which isn't needed to the view? Also you get a better overview which functions is internally when only using function funcName().
You shouldn't use the $scope to declare every function you are using, especially if it's not connected to the view.
However, there are some cases you need to use the $scope in a function not connected to view, for example if you want to emit/receive/broadcast a message on the scope tree or access something on a parent scope (although it's not necessarly a good practice).
Related
If the right way to share data between controllers are using factory/service, what is the purpose of the $rootScope?
$rootScope exists, but it can be used for evil
Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope. Since the scopes inherit from the root scope, these values will be available to the expressions attached to directives like ng-show just like values on your local $scope.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
-- AngularJS FAQ
As per my understanding.
you can use $rootScope in multiple places .
global settings defined in factory and then in view you can update as per your condition. f.x layout manipulation
you can assigned $state on run.
you can handle error ($rootScope.$on(...)
I hope this will help.
Thanks
As far as I know that basically scope is an instance of a controller.
Every time I declare a controller scope will be available for that controller.
But then why directive has scope in link function ?
I didn't declare any controller for the directive.
Then why link function has scope ? Any Idea ?
From the doc:
scope:
The scope to be used by the directive for registering watches.
You may also be interested to see the differences between $scope and scope.
All directives have a scope associated with them. They use this scope for accessing data/methods inside the template and link function. By default, unless explicitly set, directives don’t create their own scope. Therefore, directives use their parent scope ( usually a controller ) as their own.
However, AngularJS allows us to change the default scope of directives by passing a configuration object known as directive definition object. A directive definition object –– let’s call it as DDO –– is a simple JavaScript object used for configuring the directive’s behaviour,template..etc. Check out AngularJS docs about DDO.
So, we can use scope inside linking function to work with parent scope, child scope or isolated scope.
You may visit this for detailed information about scope inside directive.
You can share the data between controller and linking function.
Sometimes we would like to access that same data in both controller and link functions in Angular directive. Usually we add this data to the scope object. This has certain problems, for example exposing it to any child scope. Add the shared properties to the controller instance itself and access in the link function.
You may also be interested in what is need of link function?
A directive is Angular's way of defining 'components': you specify how its markup looks and how it behaves before, during and after being rendered. The link functions allow you to hook up events to the DOM element before or after it gets 'linked' (see also: pre-link and post-link methods). When these events are fired you might want to change some of the variables within the scope, and that's why you have access to it within the link functions.
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!
Use case
For use in a form, I created a directive that tracks changes in an array. It allows changes to be reverted and deletions and additions to be stored separately. It allows for an array (one to many mapping in the database) to be updated incrementally (rather than requiring the server to either diff, or rewrite the entire list).
Problem?
My question is about the way I expose the functionality to the controller's scope. I currently use an two-way databound attribute on the directive's scope. This works, and it seems reliable (of course you can easily break it by reassigning the scope's value, but intentionally you can break anything).
Code
You can see this plunk to see this in action. It allows methods on the directive's controller to be called from the view and the view's controller. (I am using the directive controller intentionally because that's what I do in my actual code for the directive to directive communication, but this could also just be placed in the linking function.)
Question
Is this way of doing it bad design? Am I completely throwing AngularJS out of the window now and hacking in my own code. Are there any better ways to expose functions from a directive (keep in mind that there'll be multiple of these in a single form).
It's very easy to pass in my-attribute="someFunction()" to have the directive be a consumer of the view controller. I can't find a better way to do the opposite and have the view controller consume from the directive.
Alternative?
I've been thinking about using a service here, in which the service will provide an object that is instanciated in the view, passed to the directive, and have the directive blurp out it's results to that object. Then in turn have the view controller consume the information from that service's object. Would this be a better approach?
There's nothing wrong with your approach. In fact built-in angular directives such as ng-form use this approach to store the controller in the scope (see the name property of ng-form) http://docs.angularjs.org/api/ng.directive:ngForm
For more re-usability though I would put the api methods on the controller and then put the controller itself in the api:
this.getChanges = function () {};
this.resetChanges = function(){};
$scope.api = this;
In directives, the main purpose of the controller is to serve as an api for other directives (if you didn't need an api for other directives you could just do everything in the link function). Doing it this way ensures the api is available both on the scope as well as to any directive that 'requires' the oneToMany directive.
Normally in all examples/source code of AngularJS modifications of scope is done in controllers. In my directive I need to get some information from another directive (or it's scope) and put it into scope (so visible in template of directive). As this information is common for all instances of this directive, using scope binding does not sound good for me.
So the only solution I found is to modify instance scope in linking function:
link: function(scope, element, attr, parentCtrl) {
scope.data = parentCtrl.someData;
}
This solution works. Plnkr example
The question: Is it ok according to AngularJS philosophy/style to modify scope in linking function or there is another solution?
Since you are creating isolate scopes in your directives (in your example plnkr), and you want to allow for parents to be 'somewhere' in the scope hierarchy (according to your comment to #MathewBerg), I believe your only option is to use the linking function to modify the scope.
(I suppose you could define methods on your MainCtrl that only the child directives should call, but enforcing that would be messy and break encapsulation).
So, to echo what #MathewBerg already said, yes, modify the scope in the directive/linking function.
Modifying scope in directives is fine. As for sharing information between directives there are a few methods. One is the way you described, where you access the parents controller and get it's data, another very similar method would be to have
scope.data = scope.$parent.data;
Instead of
scope.data = parentCtrl.someData;
The general way to share stuff between directives though is to use a service. This would allow you to inject the service into each directive and they can share the values. The problem with your initial method (and the one that I've described) is that if you ever move the element around so that the hierarchy of scopes change, your code will break. This is why I would recommend using a service over both. I suggest reading up on the service docs. There's also plenty of videos out there describing how to set them up: http://www.youtube.com/watch?v=1OALSkJGsRw