This video tutorial covers communicating and sharing state between controllers using a shared service and $rootScope.
You can grab the code here:
http://jsfiddle.net/simpulton/XqDxG/
With a BONUS fiddle using a directive
http://jsfiddle.net/simpulton/GeAAB/
If you have any technical questions check out the awesome mailing list here:
https://groups.google.com/forum/?fromgroups#!forum/angular
Thanks for the tutorial and the JsFiddle snippet 🙂 ((The fiddle was fairly self explaining I think))
why did you declare your service with the factory method as opposed to a service method?
factory is a shortcut method when you only need a $get function while service also a shortcut method but takes a constructor function.
Slight difference but they essentially do the same thing.
You can read more about it here.
http://docs.angularjs.org/api/AUTO.$provide
I came across a strange problem. When the handleClick() function is executed by angular (using ng-click) than the broadcasting and digesting works like a charm.
As soon as I call the same function (handleClick()) by an event click event that has been captures through jquery only the broadcasting works but no scope is digested.
I set up an example by modifying your fiddle: http://jsfiddle.net/flek/AX2z8/3/
Broadcasting with the broadcast-button works nicely but see what happens when you hit “test2”. To be sure that the variables are actually set just click the first broadcast button right after you clicked test2.
Did I got something wrong?
Hi Fritz – I moved the $scope.handleClick function out of the $scope.on function and it started working.
Prior to that, the link function was not able to see that function on the scope object. I BELIEVE function level scoping is the culprit here…
But if the link function couldn’t see the handleClick function how is it possible that the controller zero still recognizes the changed message?
Just press test2 > Broadcast 1 (without giving any inputs) then you will notice that at the end you have test2 in the first input field. This one comes from test2 (as the button says :>) Onces you uncomment $scope.$digest() it will work immediately. I’m confused 😀
Updated fiddle: http://jsfiddle.net/flek/s2Sue/
Anyway thanks for the nice screencast!
There are some events outside of the AngularJS “domain” such a DOM events that require you to tell the framework that something happened.
I highly recommend reading about the scope life-cycle here http://docs.angularjs.org/api/ng.$rootScope.Scope
With that said use $apply instead of $digest. From the docs…
“$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). ”
and
“Usually you don’t call $digest() directly in controllers or in directives. Instead a call to $apply() (typically from within a directives) will force a $digest().”
I hope this helps and maybe a blog post breaking down this very thing would be helpful. I know I was confused the first time I ran into it. Thanks for reading!
I read this page 10x but I somehow skipped $apply(). Thanks man! Now it’s working like a charm 🙂
Very nice tutorial. I must commend you on the cleanliness of your code. Seeing you code gave me the ability to go back and change up some of my “learning AngularJS” example code
Hi,
I had the same problem, here’s how I solved it: http://jsfiddle.net/g/XqDxG/146/
Guillaume
Fix TWO message: http://jsfiddle.net/g/XqDxG/147/
Hi, I tried your example, no probs, but when I convert it to a ngView based solution, there are issues with the broadcast. I have posted the issue in AngularJS mailing list and the code for the example to illustrate the issue
https://groups.google.com/forum/?fromgroups=#!topic/angular/6PRDBJ2ugXA
https://bitbucket.org/deshartman/angularjs-ctrlcomms/src (master branch)
I altered the above example to not use broadcasts, but a common factory on the CommCtrl branch to illustrate the solution
Des
Thanks for the example. It’s really helpful!
Why not having just 1 function?
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
$rootScope.$broadcast(‘handleBroadcast’);
};
Is there some deep thought under it? I tried to modify th fiddle and it still worked
Hi Ksenia — no ‘deep thought’ at all 😀
I only wanted to break the two functions out to emphasize that one function was ‘incoming’ and the other was ‘outgoing’. Normally, I would just combine the two.
Thanks for this. It has been a big help for me. I have a question regarding your controllers. I am trying to figure out what parameters you can pass them and in what order. It has not been easy! I have declaration like this, that I found with great difficulty:
function myCtrl($scope, $http)
Your demo has like this:
function ControllerZero($scope, sharedService)
If I want both $http and sharedService, how would I do that? Are there any rules of how this works?
Hi Scott —
Good question! There are two answers to this question depending on how you write your code.
The first answer is that it does not matter. AngularJS is smart enough to figure out based on the signature to inject what it needs so ControllerZero($scope, sharedService, $http) will work just as well as ControllerZero($scope, $http, sharedService).
EXCEPT! When you are minifying your code and then DI just falls apart and you have to do manual injection via ControllerZero.$inject = [‘$scope’, ‘sharedService’, ‘$http’]; etc in which case it is like a normal function call and the parameters need to line up.
Ah! Thanks for the explanation. Now I understand what you were talking about in the video.
Another question. Do you need to broadcast from the root scope if you’re only interested in communicating with child controllers? The docs seem to indicate this should work.
Any scope can send out an event but then you get into the question of direction. Do you send the event up or down ie $emit vs $broadcast? I prefer to use $rootScope as an event bus since it is always down ($broadcast).
Hi Simpulton
In your video of sharing data between controllers using a Service, you have the controllers defined in the same view.
If the controllers are attached to different HTML views and they are switched by ngView and routeProvider, will the service which is defined to share data between controllers of different views be able to share?
-thallu
Hi Thalapathy – your services live through the entire life cycle of your AngularJS application that is why they are perfectly suited to store and share your domain model.
So to your answer your question, yes as long as you inject the service into your controllers you wil be able to share data between them. That is one reason why I love dependency injection so much. It is super easy to get the information you want – just inject the appropriate service.
Hope this helps!
Hi there. I came across your video while learning AngularJS. This video pointed me in the wrong direction, so I wanted to reach out and post a comment in hopes of making this content more relevant for people who are coming to it from the same place I was.
This tutorial was very helpful in explaining how to implement modules and how to inject services from that module into a controller, so I thank you for that.
My first bit of confusion came when trying to understand why you decided to store the string value inside the sharedService object. Then at 11:04 in the video you retrieve it. But the retrieval is the only reason you’re injecting the service into ControllerTwo and ControllerThree. My thought was that with lots of simultaneous events, that sharedService.message object might get overwritten before it bubbles to the right controller.
It turns out that every Controller’s $scope object is linked together already. And $broadcast() allows you to send data down to any child $scope.$on() method and $emit() allows you to send data up the chain to any parent $scope.$on() method. So it seems that you’re climing all the way up to the highest rung on the ladder ($rootScope), and pushing an event to each child until one who’s listen for it is found. Then its that child’s job to go back and retrieve the variable. Even with that logic, there’s no need to inject all the Controllers with the service. Here is my basic implementation of your video tutorial into its most basic required parts http://jsbin.com/uvuxef/1/edit This shows the data being attached to the $broadcast() method and retrieved in each of the $on() callbacks.
To help explain this further, I’ve written an article that explains how to use $emit() and $broadcast() without needing the sharedService. http://motionharvest.com/2013/03/21/passing-arugments-to-on-in-angularjs-from-emit-and-broadcast/
I hope this helps to explain how to communicate back and forth between controllers.
Best,
Aaron
Aaron – I think your point is totally valid in a fire and forget context. This is how I used to do it back in the Flash/Flex days. Keeping in mind that my example is the simplest possible incarnation of a much larger implementation… here are a few things to consider…
1. Controllers should NEVER hold your domain model. So what happens if you need to store the value you are broadcasting? Where is the single source of truth? What happens if you need to perform an imperative operation that value before sharing that data? Who is responsible?
2. What happens if two controllers are siblings? $emit is not going to work in this case. Things can fall apart when you implicitly assume relations between controllers or assume controllers are the listeners which is why I believe you should explicitly define that relationship via an eventbus ie $rootScope. Another caveat is what happens if you want another service to listen to that event?
3. You are also exposing implementation details of your controllers to other controllers by not defining the events in a service that they share.
4. By using a service, you can now create a mock and test your implementation as well.
Another good write up on events in AngularJS… http://eburley.github.com/2013/01/31/angularjs-watch-pub-sub-best-practices.html
Yeah, those are good points. Can you recommend the best way to separate the model from the controllers? Also, for laughs, here’s one of the steps between your tutorial, and the article I wrote while trying to understand “how” AngularJS works, and “why” you suggested the solution you did: http://jsbin.com/egudiw/19/edit
This is JUST my opinion but this is how I approach it…
1. I only put methods and properties in a controller that the view it controls needs. Controllers should be super lightweight and specific.
2. Anything that needs to persist across the entire application ie domain model I put into a service.
3. Some third party libraries attach to global scope and I will wrap them in a service as well so I access them explicitly and not implicitly. Socket.io is a great example of where you would want to do this.
I think the easiest way to delineate what goes where is by focusing on the specificity of the relationship between the View and the ViewModel [controller + $scope]. If a method or property is not specific to that implementation then I would consider it a candidate to move to a service. Hope this helps!
PS Good conversation! I really love how you are digging into this with examples and playing around with it. High five!
That approach makes sense. *High five reciprocated*
I have a project coming up that I’m preparing for. I’ll be getting a load of XML from a service and parsing it into a JSON object. During parsing, based on the structure of the XML, I’ll be grabbing various templates to construct the UI of the application.
As I understand it, my UI elements will link up the controller functions I define, and the events they fire will either $emit or $broadcast depending on if they are controlling child elements, or alerting parent elements of their status. I was thinking that the JSON object that contains the parsed XML would be its own thing, outside the clutches of AgularJS, but it sounds like you suggest wrapping that model inside a service- and $injecting it to all of the controllers. Which actually makes sense.
What I’m unsure about is how to populate the view via the model at the time the UI is constructed. But since there are no “constructors” how do i get data into it?
For communicating between the angular controllers look at this post: http://coding-issues.blogspot.in/2013/11/call-controller-from-another-controller-angularjs.html
Nice tutorial, very easy and explained in a simple way. Thanks a lot.
i am trying to access data from my controller under different app from another controller under diff app , is that possible
requirement
• I make a rest call from index.html , which returns a JSON
• I would like to populate data from this JSON in 3 different pages , without making a call again
In Angular JS , is it possible to share data across views (pages) not in different controllers in the same page
Can you put this in a plunk so I can see what you are trying to accomplish?
why don’t you use just service to share data???
http://jsbin.com/xebife/3/edit?html,js,output
I think that is an entirely appropriate approach as well. Thanks for sharing!
Hello, Thanks for this excellent example in jsFiddle and video ;D
the three controllers are in the same module.If i want to share data between two different controllers in two different module, how can i do it??
As long as all your modules are unified by a top-level controller, this should work.
Hi,
It works fine but if i have a switch in the html and when i load one view from other for suppose there is a favorite view and on click of the home button in the top i switch the view then in the home view’s controller the $on function is not called for the broadcast event.
A better way would be to use the publish/subscribe model of angularjs.
Why is a service required to do this ?