Still on the basics of AngularJS, i understand the concepts, however, just looking at custom events,
$emit
and
$broadcast
for nested controller..
According to the docs, $emit bubbles the event, ie. passes it up the chain, for nested controllers,
My question, is, how is this different to just simply calling a function from the parent controller (prototypical inheritance). Or have i miss understood something?
The thing is, you can't always be certain that your direct parent, is the one you want to call. It's easy to break your code with that kind of anti-pattern.
And you must agree that
$scope.$parent.$parent.$parent.doSomething();
Is pretty ugly.
Instead you can $emit an event upwards, now it doesn't matter how far up the chain your parent controller is, as long as it is listening and reacting.
This gives you nice loose coupling between controllers, and just acts like a message pump.
The same goes for $broadcast, just downwards instead, and here I would argue that it is even more important.
Take the example of a child controller with many parents or a parent controller with many children. Should the developer need to maintain a list of children within the parent to invoke a function on each of them. $emit and $broadcast are utilities to allow a loose-coupling messaging along the lines of an Observer pattern. If all parents in the hierarchy need to know that a child controller has done some task or needs some task dome on its behalf then is can just generate an event and interested parties can listen.
Data can also be passed removing the need for controllers to share data on the inherited scope.
$emit helps you to pass event to the parent controllers.
You can't use $controller('ParentController', {scope: scope}) in all your child controllers to inherit the properties. To make the code clean and loosely coupled, $emit will help you to achieve that.
Assume you have three levels of hierarchy and you want the child controller to update the value of a particular parent controller. If you are going to do that via prototype chain, you need to inherit that particular controller using $controller('ParentController', {scope: scope}) but $emit will avoid that. You need not know which parent controller. instead just emit the event.
In the appropriate parent controller where you want to read the data, use
$scope.$on("eventname", function(event, data) {
// update value here
});
Related
I'm trying to figure out what would be the best way to perform cleanup on different angular directives.
I have different types of directives, some do not define their own scope, some have an isolated scope, and some have a child scope.
I need a generic mechanism that will take care of the cleanup in a separate component that my directives use.
So basically I'm looking at two different options, either register on angular's element.on('$destroy', function() {...}) or on jquery's scope.$on('$destroy', function () {...}).
Here's the problem:
If I register on the underlying element destruction then I miss destruction of directives that they're element was not destructed (not sure exactly how's that possible, noticed it via testing...).
If I register on the underlying scope destruction then (I think) I miss destruction of directives that they're element was destructed, for instance when the directive is not defining its own scope and is using its parent scope.
Looking at angular's directives documentation I came across this:
Best Practice: Directives should clean up after themselves. You can use element.on('$destroy', ...) or scope.$on('$destroy', ...) to run a clean-up function when the directive is removed.
There's no mention as far as I can tell about which strategy to use when.
Also, looking at Angular's documentation I came across this:
When child scopes are no longer needed, it is the responsibility of the child scope creator to destroy them via scope.$destroy() API.
I don't understand the scenario in which I am supposed to call the scope.$destroy() API on my own.
In vue2.0 the event $dispatch and $broadcast is deprecated.
And I found that $dispatch is similar with $emit.
What's the differences between them? Is it safe directly replacing $dispatch into $emit when migrating.
No, You will not be able to replace $disptach with $emit everywhere. You can replace it, wherever you are using it for communication from child to parent, but for other cases, you may have to take some other approach.
From the documentation ( similar comments from Evan you in Upgrade Tips):
One of the most common uses for these methods is to communicate between a parent and its direct children. In these cases, you can actually listen to an $emit from a child with v-on. This allows you to keep the convenience of events with added explicitness.
However, when communicating between distant descendants/ancestors, $emit won’t help you. Instead, the simplest possible upgrade would be to use a centralized event hub.
From the documentation of $dispatch
Dispatch an event, first triggering it on the instance itself, and then propagates upward along the parent chain. The propagation stops when it triggers a parent event listener, unless that listener returns true.
on the other hand $emit:
Trigger an event on the current instance. Any additional arguments will be passed into the listener’s callback function.
So you can see, if you are passing communication to multiple layer of parent elements via $dispatch, you have to handle that code differently with $emit
I need some guidance to take the best practice for my task in AngularJS.
Task:
Inside the view : I have one parent controller and two child controllers.
Child controllers work with their own $scope and objects.
When I press save in the view, I need to get the data from child controllers to parent controller in order to prepare an object for posting it to the server.
I am getting confused of what is the best solution for this approach.
A common way of sharing data between controllers is to use use a service.
You could also broadcast updates to the parent controller
There are many different ways to achieve this..
Using $rootScope.
Using Services
Use broadcast and emit
Declare an object in parent controller and then modify the same object in the child controller.
Using $rootScope is not a good approach, as the $rootScope variable is destroyed when application is closed.
I will also not recommend broadCast or emit, until it's required.
Services, is good for communication b/w controllers, but again you have inject it and modify the methods.
In your, scenario, i would recommend to use the common $scope object variable, which is declared inside parent and used in child controllers, as all methods are inherited in the child controllers.
Above answers are useful, but below is an simple way to update a $scope object in parent controller from child scope as below.
In child controller, $scope.$parent.parentObject = updatedObject
Hope it helps!
There are three common ways to share data between controllers:
1 put data in service or factory. Since they are singleton, any controller injected with same service can share the data within that service
2 put data in $rootScope
3 use $broadcast or $emit to send events between scopes, and pass the shared data as event arguments
So I have two directives splitter and pane that may be used like so:
<splitter>
<pane></pane>
<pane></pane>
</splitter>
<splitter>
</splitter>
I want them all to have isolate or inherited scope. However I also want to be able to $broadcast (or equivalent) between them so that if I were to $broadcast on one directive's scope, the same event would be triggered on all the nested directives that are listening but not it's parent or siblings (no $rootScope here).
How would one go about doing this? My solution must be future friendly as I will be adding more directives in to the mix which also listen for this event.
To do inter-directive communication, the best is to use the parent directive controller and expose methods in this.
Then you just have to require it in your children directive (require: '^splitter') and the parent controller will be injected as the fourth argument of your link function.
For more information, you can see the official documentation about Creating Directives that Communicate.
I have a problem reasoning about components communication.
The main question I tried to reason about and failed: what should I use - $watch or $on ($broadcast/$emit) to establish the communication between components?
I see three basic cases:
Controller + Directive. They communicate naturally using the $scope bidibinding. I just put the service, which incapsulates some shared state, in the $scope using some object ($scope.filter = {}).
This approach seems very reasonable to me.
Controller + Controller. I use the DI to inject singleton services with incapsulated state to communicate between controllers. Those services are bounded to directives using the previous approach. This gives me the data binding out-of-the-box.
Directive + Directive. This is the blind spot in my reasoning. I have directives, that reside in different scopes, in the same scope, etc.
I have directives that must reflect all changes (think about slider + charts) and directives, that must trigger the http request (think about select input).
So, the questions are:
What should I use - $watch or $on ($broadcast/$emit) to establish the communication between components?
Should I tend to use $watch in directive-to-directive communication?
Or should I tend to use $broadcast in directive-to-directive case?
Is it better to share the state using injection+binding or injection+events?
I think this depends on the use case for your directives/components. If you expect to be able to re-use a component without having to modify the scope that the component lives in then using broadcast/emit/on would make more sense. IMO if a component internally has some information that I want to be able to retrieve and do different things with, then the broadcast/emit/on scheme makes the most sense.
If on the other hand I need to trigger some service calls in response to something in a directive or I want to share state between a couple of views I end up using a service.
As noted in the comments another alternative that exists is using the require property in the directive definition object:
require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent']
require - Require another directive and inject its controller as the
fourth argument to the linking function. The require takes a string
name (or array of strings) of the directive(s) to pass in. If an array
is used, the injected argument will be an array in corresponding
order. If no such directive can be found or if the directive does not
have a controller, then an error is raised. The name can be prefixed
with:
(no prefix) - Locate the required controller on the current element.
? - Attempt to locate the required controller, or return null if not found.
^ - Locate the required controller by searching the element's parents.
?^ - Attempt to locate the required controller by searching the element's parents, or return null if not found.
This can be useful in cases where you're creating a "compound component" where multiple directives make more sense than trying to encapsulate all of the functionality into one directive, but you still require some communication between the "main/wrapping directive" and it's siblings/children.
I know this isn't a clear cut answer but I'm not sure that there is one. Open to edits/comments to modify if I'm missing some points.