AngularJS – Communicating Between Controllers

This video tutorial covers communicating and sharing state between controllers using a shared service and $rootScope.


Download YouTube Video | Convert YouTube to MP3

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

AngularJS – Communicating Between Controllers

30 Responses

  1. Thanks for the tutorial and the JsFiddle snippet :-) ((The fiddle was fairly self explaining I think))

    KajMagnus June 7, 2012 at 7:04 pm #
  2. why did you declare your service with the factory method as opposed to a service method?

    joel July 31, 2012 at 5:40 pm #
  3. 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

    simpulton July 31, 2012 at 5:52 pm #
  4. 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?

    Fritz August 8, 2012 at 4:16 pm #
  5. 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…

    simpulton August 8, 2012 at 4:37 pm #
  6. 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 :D
    Updated fiddle: http://jsfiddle.net/flek/s2Sue/

    Anyway thanks for the nice screencast!

    Fritz August 8, 2012 at 9:27 pm #
  7. 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!

    simpulton August 8, 2012 at 11:22 pm #
  8. I read this page 10x but I somehow skipped $apply(). Thanks man! Now it’s working like a charm :)

    Fritz August 9, 2012 at 4:51 pm #
  9. 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

    Kevin August 25, 2012 at 1:21 am #
  10. Hi,

    I had the same problem, here’s how I solved it: http://jsfiddle.net/g/XqDxG/146/

    Guillaume

    Guillaume September 13, 2012 at 5:15 pm #
  11. Fix TWO message: http://jsfiddle.net/g/XqDxG/147/

    Guillaume September 17, 2012 at 10:59 am #
  12. 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

    Des Hartman October 4, 2012 at 6:46 am #
  13. 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

    Ksenia November 22, 2012 at 1:45 pm #
  14. Hi Ksenia — no ‘deep thought’ at all :D

    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.

    simpulton November 22, 2012 at 9:47 pm #
  15. 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?

    Scott November 25, 2012 at 6:16 am #
  16. 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.

    simpulton November 25, 2012 at 6:23 am #
  17. Ah! Thanks for the explanation. Now I understand what you were talking about in the video.

    Scott November 25, 2012 at 7:40 am #
  18. 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.

    Scott November 25, 2012 at 7:42 am #
  19. 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).

    simpulton November 25, 2012 at 7:48 am #
  20. 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

    Thalapathy Krishnamurthy February 7, 2013 at 11:55 am #
  21. 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!

    simpulton February 7, 2013 at 4:13 pm #
  22. 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 S. March 21, 2013 at 6:33 pm #
  23. 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

    simpulton March 21, 2013 at 7:16 pm #
  24. 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

    Aaron S. March 21, 2013 at 9:30 pm #
  25. 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!

    simpulton March 21, 2013 at 10:03 pm #
  26. 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?

    Aaron S. March 21, 2013 at 11:18 pm #
  27. For communicating between the angular controllers look at this post: http://coding-issues.blogspot.in/2013/11/call-controller-from-another-controller-angularjs.html

    ranadheer November 5, 2013 at 12:33 pm #
Trackbacks/Pingbacks
  1. Angular Cats! Part 3 – Communicating with $broadcast » Eric Terpstra - September 18, 2012

    [...] communicate clicks and cats between controllers, I found a dead simple solution that cobbles together an event dispatcher-esque system. When ‘eventBroadcast’ is [...]

  2. AngularJS communicating between controllers. | ??????? - July 1, 2013

    [...] <a href=”http://onehungrymind.com/angularjs-communicating-between-controllers/”>http://onehungrymind…; [...]

  3. Sharing data between controllers in AngularJS (PubSub/Event bus example) | mariuszprzydatek.com - December 28, 2013

    […] One Hungry Mind webcast (http://onehungrymind.com/angularjs-communicating-between-controllers) […]

Leave a Reply