I'm trying to share data across controllers. Use-case is a multi-step form, data entered in one input is later used in multiple display locations outside the original controller. Code below and in jsfiddle here.

HTML

<div ng-controller="FirstCtrl">
    <input type="text" ng-model="FirstName"><!-- Input entered here -->
    <br>Input is : <strong>{{FirstName}}</strong><!-- Successfully updates here -->
</div>

<hr>

<div ng-controller="SecondCtrl">
    Input should also be here: {{FirstName}}<!-- How do I automatically updated it here? -->
</div>

JS

// declare the app with no dependencies
var myApp = angular.module('myApp', []);

// make a factory to share data between controllers
myApp.factory('Data', function(){
    // I know this doesn't work, but what will?
    var FirstName = '';
    return FirstName;
});

// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){

});

// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
    $scope.FirstName = Data.FirstName;
});

Any help is greatly appreciated.

9 Answers 11

up vote 433 down vote accepted

A simple solution is to have your factory return an object and let your controllers work with a reference to the same object:

JS:

// declare the app with no dependencies
var myApp = angular.module('myApp', []);

// Create the factory that share the Fact
myApp.factory('Fact', function(){
  return { Field: '' };
});

// Two controllers sharing an object that has a string in it
myApp.controller('FirstCtrl', function( $scope, Fact ){
  $scope.Alpha = Fact;
});

myApp.controller('SecondCtrl', function( $scope, Fact ){
  $scope.Beta = Fact;
});

HTML:

<div ng-controller="FirstCtrl">
    <input type="text" ng-model="Alpha.Field">
    First {{Alpha.Field}}
</div>

<div ng-controller="SecondCtrl">
<input type="text" ng-model="Beta.Field">
    Second {{Beta.Field}}
</div>

Demo: http://jsfiddle.net/HEdJF/

When applications get larger, more complex and harder to test you might not want to expose the entire object from the factory this way, but instead give limited access for example via getters and setters:

myApp.factory('Data', function () {

    var data = {
        FirstName: ''
    };

    return {
        getFirstName: function () {
            return data.FirstName;
        },
        setFirstName: function (firstName) {
            data.FirstName = firstName;
        }
    };
});

With this approach it is up to the consuming controllers to update the factory with new values, and to watch for changes to get them:

myApp.controller('FirstCtrl', function ($scope, Data) {

    $scope.firstName = '';

    $scope.$watch('firstName', function (newValue, oldValue) {
        if (newValue !== oldValue) Data.setFirstName(newValue);
    });
});

myApp.controller('SecondCtrl', function ($scope, Data) {

    $scope.$watch(function () { return Data.getFirstName(); }, function (newValue, oldValue) {
        if (newValue !== oldValue) $scope.firstName = newValue;
    });
});

HTML:

<div ng-controller="FirstCtrl">
  <input type="text" ng-model="FirstName">
  <br>Input is : <strong>{{FirstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
  Input should also be here: {{FirstName}}
</div>

Demo: http://jsfiddle.net/27mk1n1o/

2 upvote
  flag
First solution worked best for me -- had the service maintain the object and the controllers just handle with the reference. Mucho thanks. – johnkeese
5 upvote
  flag
the $scope.$watch method works beautifully for making a rest call from one scope and applying the results to another scope – aclave1
upvote
  flag
Instead of returning an object like return { FirstName: '' }; or returning an object containing methods that interact with an object, would it not be prefferable to return an instance of a class(function) so that when you use your object, it has a specific type and it is not only an "Object{}" ? – RPDeshaies
8 upvote
  flag
Just a heads up, the listener function doesn't need the newValue !== oldValue check because Angular doesn't execute the function if the oldValue and newValue are the same. The only exception to this is if you care about the initial value. See: docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch – Gautham C.
upvote
  flag
@tasseKATT , It works good ,if I don't refresh the page . Can you suggest me any solution if I refresh the page ??? – Mann
upvote
  flag
To add to this answer, I would think of the service as the way to manage the common data. The controllers can use the data from the service and call service methods for operations (updates etc). A simple example can be found here: ozkary.com/2015/08/angularjs-sharing-data-across.html – ozkary
upvote
  flag
@tasseKaTT does that mean in order to able to share data , I have to $watch each single of them ? – cjmling
upvote
  flag
If you go with the second solution and want to put the control on property level, yes. Honestly though, I have almost never used the second solution in any of the applications I've built and I will probably update this answer when I get time. – tasseKATT
upvote
  flag
@tasseKATT please use more unique names in your illustrational code, so newcomers and beginners see what is what. Calling everything Data is confusing when learning, as someone can't know if it is a builtin data type, like the data variable returned from $http for example - or a variable that has been declared in different scopes. Please use some unofficially sounding unique names, eg. asdf, xyz ... instead of data. – Király István
upvote
  flag
@KirályIstván The answerer has taken the time to answer. If you feel that the code can be improved please feel free to suggest edits to the code. – Ryan.lay
upvote
  flag
The second approach worked wonders for me! Thanks for a really nice explanation – shivisuper
upvote
  flag
Does this work if the controllers are modularized? – natureminded
upvote
  flag

I prefer not to use $watch for this. Instead of assigning the entire service to a controller's scope you can assign just the data.

JS:

var myApp = angular.module('myApp', []);

myApp.factory('MyService', function(){
  return {
    data: {
      firstName: '',
      lastName: ''
    }
    // Other methods or objects can go here
  };
});

myApp.controller('FirstCtrl', function($scope, MyService){
  $scope.data = MyService.data;
});

myApp.controller('SecondCtrl', function($scope, MyService){
   $scope.data = MyService.data;
});

HTML:

<div ng-controller="FirstCtrl">
  <input type="text" ng-model="data.firstName">
  <br>Input is : <strong>{{data.firstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
  Input should also be here: {{data.firstName}}
</div>

Alternatively you can update the service data with a direct method.

JS:

// A new factory with an update method
myApp.factory('MyService', function(){
  return {
    data: {
      firstName: '',
      lastName: ''
    },
    update: function(first, last) {
      // Improve this method as needed
      this.data.firstName = first;
      this.data.lastName = last;
    }
  };
});

// Your controller can use the service's update method
myApp.controller('SecondCtrl', function($scope, MyService){
   $scope.data = MyService.data;

   $scope.updateData = function(first, last) {
     MyService.update(first, last);
   }
});
upvote
  flag
You don't use explicitly watch() but internally AngularJS does to update the view with new values in $scope variables. In terms of performance it's the same or not? – Mart Dominguez
3 upvote
  flag
I'm not sure on the performance of either method. From what I understand Angular is already running a digest loop on the scope so setting up additional watch expressions probably adds more work for Angular. You would have to run a performance test to really find out. I just prefer data transfer and sharing via the answer above over updating via $watch expressions. – bennick
upvote
  flag
I don't get how this is an alternative to $watch. $watch is like a push to all watchers, that the data changes. You make manual updates which aren't usefull if you just want to update the master data in the factory. Or am I missing something? – Sumit
1 upvote
  flag
This is not an alternative to $watch. It is a different way to share data between two Angular objects, in this case controllers, without having to use $watch. – bennick
upvote
  flag
IMO this solution is the best one because it is more in the spirit of Angular's declaritive approach, as opposed to adding $watch statements, which feels a bit more jQuery-esque. – JamesG
5 upvote
  flag
Why isn't this the most voted answer I wonder. After all $watch makes the code more complex – Shyamal Parikh
upvote
  flag
Agreed. I love the declarative ideal of this approach - especially since Todd Motto's opinionated style guide recommends limiting your use of $scope.$watch. – dpg5000
upvote
  flag
@bennick are there any issues to be aware of when using a data declaration like this as opposed to a nominal $scope.$watch when sharing data between Angular objects? This seems so elegant, i'm not sure why it's not more widely used... – dpg5000
upvote
  flag
@dpg5000 Not that I'm aware of. Anything that I inject a service into (controller, another service) is sharing the same singleton object (our injected service). So when the service object it updated it is immediately reflected in other objects that depend on it. I have found this to extremely reliable. – bennick
upvote
  flag
This is the best solution. Using adding $watches can rapidly lead to performance issues especially in an ng-repeat. – Ryan.lay

I've created a factory that controls shared scope between route path's pattern, so you can maintain the shared data just when users are navigating in the same route parent path.

.controller('CadastroController', ['$scope', 'RouteSharedScope',
    function($scope, routeSharedScope) {
      var customerScope = routeSharedScope.scopeFor('/Customer');
      //var indexScope = routeSharedScope.scopeFor('/');
    }
 ])

So, if the user goes to another route path, for example '/Support', the shared data for path '/Customer' will be automatically destroyed. But, if instead of this the user goes to 'child' paths, like '/Customer/1' or '/Customer/list' the the scope won't be destroyed.

You can see an sample here: http://plnkr.co/edit/OL8of9

upvote
  flag
Use $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){ ... }) if you have ui.router – Nuno Silva

Just do it simple (tested with v1.3.15):

<article ng-controller="ctrl1 as c1">
    <label>Change name here:</label>
    <input ng-model="c1.sData.name" />
    <h1>Control 1: {{c1.sData.name}}, {{c1.sData.age}}</h1>
</article>
<article ng-controller="ctrl2 as c2">
    <label>Change age here:</label>
    <input ng-model="c2.sData.age" />
    <h1>Control 2: {{c2.sData.name}}, {{c2.sData.age}}</h1>
</article>

<script>
    var app = angular.module("MyApp", []);

    var dummy = {name: "Joe", age: 25};

    app.controller("ctrl1", function () {
        this.sData = dummy;
    });

    app.controller("ctrl2", function () {
        this.sData = dummy;
    });
</script>

5 upvote
  flag
I think this was down-voted because it's not modular. dummy is global to both controllers rather than shared in a more modular way via inheritance with $scope or controllerAs or using a service. – Chad Johnson

Not sure where I picked up this pattern but for sharing data across controllers and reducing the $rootScope and $scope this works great. It is reminiscent of a data replication where you have publishers and subscribers. Hope it helps.

The Service:

(function(app) {
    "use strict";
    app.factory("sharedDataEventHub", sharedDataEventHub);

    sharedDataEventHub.$inject = ["$rootScope"];

    function sharedDataEventHub($rootScope) {
        var DATA_CHANGE = "DATA_CHANGE_EVENT";
        var service = {
            changeData: changeData,
            onChangeData: onChangeData
        };
        return service;

        function changeData(obj) {
            $rootScope.$broadcast(DATA_CHANGE, obj);
        }

        function onChangeData($scope, handler) {
            $scope.$on(DATA_CHANGE, function(event, obj) {
                handler(obj);
            });
        }
    }
}(app));

The Controller that is getting the new data, which is the Publisher would do something like this..

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);

The Controller that is also using this new data, which is called the Subscriber would do something like this...

sharedDataEventHub.onChangeData($scope, function(data) {
    vm.localData.Property1 = data.Property1;
    vm.localData.Property2 = data.Property2;
});

This will work for any scenario. So when the primary controller is initialized and it gets data it would call the changeData method which would then broadcast that out to all the subscribers of that data. This reduces the coupling of our controllers to each other.

upvote
  flag
using a 'model' that shares the state between controllers seems like the option that is mostly used. Unfortunately this doesn't cover the scenario where you would refresh the child controller. How do you 'refetch' the data from the server and recreate the model ? And how would you deal with cache invalidation ? – Andrei
upvote
  flag
I think the idea of parent and child controllers is a bit dangerous and causes too many dependencies on each other. However, when used in combination with UI-Router, you can easily refresh the data on your "child", especially if that data is injected via the state configuration. Cache invalidation happens on the publisher whereby they would be responsible for retrieving the data and calling sharedDataEventHub.changeData(newData) – ewahner
upvote
  flag
I think there is a misunderstanding. By refresh i mean the users literally press the F5 key on the keyboard and causes the browser to do a post back. If you are in the child/details view when this happens, there is no one to call "var someData = yourDataService.getSomeData()" . The question is, how would you deal with this scenario ? who should update the model ? – Andrei
upvote
  flag
Depending on how you activate your controllers this could be very simple. If you have a method that activates then the controller that is responsible for initially loading the data, would load it and then use the sharedDataEventHub.changeData(newData) then any child or controller that depends on that data would have somewhere in their controller body the following: sharedDataEventHub.onChangeData($scope, function(obj) { vm.myObject = obj.data; }); If you used UI-Router this could be injected in from the state config. – ewahner
upvote
  flag
Here is an example that ilustrates the problem that i am trying to describe: plnkr.co/edit/OcI30YX5Jx4oHyxHEkPx?p=preview . If you pop out the plunk, navigate to the edit page and refresh the browser, it will crash. Who should recreate the model in this case ? – Andrei
upvote
  flag
The page does not crash for me. Perhaps its browser specific. Also in an SPA a refresh should never crash your application. You are using UI Router, so why not inject your startup data into the controller with a resolve? – ewahner
upvote
  flag

There is another way without using $watch, using angular.copy:

var myApp = angular.module('myApp', []);

myApp.factory('Data', function(){

    var service = {
        FirstName: '',
        setFirstName: function(name) {
            // this is the trick to sync the data
            // so no need for a $watch function
            // call this from anywhere when you need to update FirstName
            angular.copy(name, service.FirstName); 
        }
    };
    return service;
});


// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){

});

// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
    $scope.FirstName = Data.FirstName;
});

There are many ways you can share the data between controllers

  1. using services
  2. using $state.go services
  3. using stateparams
  4. using rootscope

Explanation of each method:

  1. I am not going to explain as its already explained by someone

  2. using $state.go

      $state.go('book.name', {Name: 'XYZ'}); 
    
      // then get parameter out of URL
      $state.params.Name;
    
  3. $stateparam works in a similar way to $state.go, you pass it as object from sender controller and collect in receiver controller using stateparam

  4. using $rootscope

    (a) sending data from child to parent controller

      $scope.Save(Obj,function(data) {
          $scope.$emit('savedata',data); 
          //pass the data as the second parameter
      });
    
      $scope.$on('savedata',function(event,data) {
          //receive the data as second parameter
      }); 
    

    (b) sending data from parent to child controller

      $scope.SaveDB(Obj,function(data){
          $scope.$broadcast('savedata',data);
      });
    
      $scope.SaveDB(Obj,function(data){`enter code here`
          $rootScope.$broadcast('saveCallback',data);
      });
    

There are multiple ways to do this.

  1. Events - already explained well.

  2. ui router - explained above.

  3. Service - with update method displayed above
  4. BAD - Watching for changes.
  5. Another parent child approach rather than emit and brodcast -

*

<superhero flight speed strength> Superman is here! </superhero>
<superhero speed> Flash is here! </superhero>

*

app.directive('superhero', function(){
    return {
        restrict: 'E',
        scope:{}, // IMPORTANT - to make the scope isolated else we will pollute it in case of a multiple components.
        controller: function($scope){
            $scope.abilities = [];
            this.addStrength = function(){
                $scope.abilities.push("strength");
            }
            this.addSpeed = function(){
                $scope.abilities.push("speed");
            }
            this.addFlight = function(){
                $scope.abilities.push("flight");
            }
        },
        link: function(scope, element, attrs){
            element.addClass('button');
            element.on('mouseenter', function(){
               console.log(scope.abilities);
            })
        }
    }
});
app.directive('strength', function(){
    return{
        require:'superhero',
        link: function(scope, element, attrs, superHeroCtrl){
            superHeroCtrl.addStrength();
        }
    }
});
app.directive('speed', function(){
    return{
        require:'superhero',
        link: function(scope, element, attrs, superHeroCtrl){
            superHeroCtrl.addSpeed();
        }
    }
});
app.directive('flight', function(){
    return{
        require:'superhero',
        link: function(scope, element, attrs, superHeroCtrl){
            superHeroCtrl.addFlight();
        }
    }
});

There are multiple ways to share data between controllers

  • Angular services
  • $broadcast, $emit method
  • Parent to child controller communication
  • $rootscope

As we know $rootscope is not preferable way for data transfer or communication because it is a global scope which is available for entire application

For data sharing between Angular Js controllers Angular services are best practices eg. .factory, .service
For reference

In case of data transfer from parent to child controller you can directly access parent data in child controller through $scope
If you are using ui-router then you can use $stateParmas to pass url parameters like id, name, key, etc

$broadcast is also good way to transfer data between controllers from parent to child and $emit to transfer data from child to parent controllers

HTML

<div ng-controller="FirstCtrl">
   <input type="text" ng-model="FirstName">
   <br>Input is : <strong>{{FirstName}}</strong>
</div>

<hr>

<div ng-controller="SecondCtrl">
   Input should also be here: {{FirstName}}
</div>

JS

myApp.controller('FirstCtrl', function( $rootScope, Data ){
    $rootScope.$broadcast('myData', {'FirstName': 'Peter'})
});

myApp.controller('SecondCtrl', function( $rootScope, Data ){
    $rootScope.$on('myData', function(event, data) {
       $scope.FirstName = data;
       console.log(data); // Check in console how data is coming
    });
});

Refer given link to know more about $broadcast

Not the answer you're looking for? Browse other questions tagged or ask your own question.