333

I have two Angular controllers:

function Ctrl1($scope) {
    $scope.prop1 = "First";
}

function Ctrl2($scope) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

I can't use Ctrl1 inside Ctrl2 because it is undefined. However if I try to pass it in like so…

function Ctrl2($scope, Ctrl1) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

I get an error. Does anyone know how to do this?

Doing

Ctrl2.prototype = new Ctrl1();

Also fails.

NOTE: These controllers are not nested inside each other.

Zanon
  • 29,231
  • 20
  • 113
  • 126
dopatraman
  • 13,416
  • 29
  • 90
  • 154
  • 1
    There are many ways but the best way is angular watch. Always when we use a framework is the best way to use her own methods for do work **dont forget this** – pejman Dec 29 '16 at 13:31
  • I found this blog very helpful [Blog](http://excellencenodejsblog.com/angularjs-sharing-data-between-controller/) – Black Mamba Sep 14 '17 at 13:02

16 Answers16

506

One way to share variables across multiple controllers is to create a service and inject it in any controller where you want to use it.

Simple service example:

angular.module('myApp', [])
    .service('sharedProperties', function () {
        var property = 'First';

        return {
            getProperty: function () {
                return property;
            },
            setProperty: function(value) {
                property = value;
            }
        };
    });

Using the service in a controller:

function Ctrl2($scope, sharedProperties) {
    $scope.prop2 = "Second";
    $scope.both = sharedProperties.getProperty() + $scope.prop2;
}

This is described very nicely in this blog (Lesson 2 and on in particular).

I've found that if you want to bind to these properties across multiple controllers it works better if you bind to an object's property instead of a primitive type (boolean, string, number) to retain the bound reference.

Example: var property = { Property1: 'First' }; instead of var property = 'First';.


UPDATE: To (hopefully) make things more clear here is a fiddle that shows an example of:

  • Binding to static copies of the shared value (in myController1)
    • Binding to a primitive (string)
    • Binding to an object's property (saved to a scope variable)
  • Binding to shared values that update the UI as the values are updated (in myController2)
    • Binding to a function that returns a primitive (string)
    • Binding to the object's property
    • Two way binding to an object's property
Gloopy
  • 37,767
  • 15
  • 103
  • 71
  • 5
    In this case - how would the scope of Ctrl2 "know" when sharedProperties.getProperty() changes value? – OpherV Apr 03 '13 at 07:39
  • 5
    If you wanted your UI to update each time the property changes you can change `both` to be a function and it will be called/re-evaluated during the angular digest process. See [this fiddle](http://jsfiddle.net/x8vfR/) for an example. Also if you bind to an object's property you can use it directly in your view and it will update as the data is changed similar to [this example](http://jsfiddle.net/tg78g/2/). – Gloopy Apr 03 '13 at 19:24
  • 12
    If you want to detect and react to changes in your controller an option is to add the `getProperty()` function to the scope and use [$scope.$watch](http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch) like in [this example](http://jsfiddle.net/tg78g/1/). Hope these examples help! – Gloopy Apr 03 '13 at 19:24
  • 1
    There is a problem here as services should be stateless. Storing off a property inside of a service is wrong (but convenient). I started using $cacheFactory to read and write data. I use almost an identical service as Gloopy but instead of storing state in the service, it is now in the cache. First create a cache service: angular.module('CacheService', ['ng']) .factory('CacheService', function($cacheFactory) { return $cacheFactory('CacheService'); }); Include in in your app.js, inject it in the service, use it like so: return CacheService.get(key); or CacheService.put(key, value); – Jordan Papaleo Oct 01 '13 at 14:47
  • @Breck421, in the first example in [Angular's own documentation](https://docs.angularjs.org/guide/services), their "notify" service stores `msgs` the same way. Seems services can be stateful after all. – adamdport Jun 05 '14 at 00:14
  • You are right; That was an October assumption and a lot has happened since then. Little did I know at the time that an angular service was a singleton. In general programming a service is a data provider, not a state persister. – Jordan Papaleo Jun 06 '14 at 04:14
  • The provided fiddle does not work - it does not update the 1st controller. Also it mixes variable with functions for unclear reason - `{{stringValue}}` inside the 1st template, but `{{stringValue()}}` inside the 2nd! – Dmitri Zaitsev Aug 09 '14 at 16:50
  • @DmitriZaitsev the fiddle was trying to demonstrate different styles of binding to primitives (which is why the first controller doesn't update properly). I tried to describe it in the Update section at the bottom of the answer. Hope this helps! – Gloopy Aug 11 '14 at 17:34
  • I have posted a solution below to create global variables without creating Service, but instead using $rootScope. – Sanjeev Aug 30 '14 at 15:26
  • @Gloopy It seems like it's a lot of work to add sharedprops.getVar() to make sure you have an updated variable (especially if you're adding this to a pre-existing controller). The closest other thing I got was to assign the function itself and then call $scope.both(), but then I still have to add () everywhere throughout my controller. Is there any way to avoid adding () at the end of everything? – Organiccat Sep 19 '14 at 14:25
  • @Organiccat if you bind to properties of an object instead of a primitive type you won't have to add () everywhere after you have the object on your scope. See [this example](http://jsfiddle.net/b2fCE/463/) hope this helps! – Gloopy Sep 19 '14 at 16:02
  • @Gloopy - seems a lots of work just to share a property, what happen if I want to share a list of property? can we do a hashMap there? any example? thanks . – Jaxox Dec 03 '14 at 21:58
  • 4
    Trying to grok how and why this answer uses `.service` instead of `.factory` as described in the Angular docs. Why is this answer voted so high when the documentation uses a different method? – pspahn Apr 06 '15 at 23:49
  • 1
    how can i config the service for each controller. (instead of module level configuration) – OMGPOP Jun 25 '15 at 06:11
  • 1
    @Jaxox sorry for the late reply you can bind to a hash see [this example](http://jsfiddle.net/b2fCE/718/). @pspahn I've read about the differences but still use `.service` and `.factory` interchangeably :| @OMGPOP can you clarify? – Gloopy Jun 25 '15 at 16:44
  • If you want to show a valuable exemple is to show how to set first before using get – Gino Mar 20 '16 at 15:57
45

I like to illustrate simple things by simple examples :)

Here is a very simple Service example:


angular.module('toDo',[])

.service('dataService', function() {

  // private variable
  var _dataObj = {};

  // public API
  this.dataObj = _dataObj;
})

.controller('One', function($scope, dataService) {
  $scope.data = dataService.dataObj;
})

.controller('Two', function($scope, dataService) {
  $scope.data = dataService.dataObj;
});

And here the jsbin

And here is a very simple Factory example:


angular.module('toDo',[])

.factory('dataService', function() {

  // private variable
  var _dataObj = {};

  // public API
  return {
    dataObj: _dataObj
  };
})

.controller('One', function($scope, dataService) {
  $scope.data = dataService.dataObj;
})

.controller('Two', function($scope, dataService) {
  $scope.data = dataService.dataObj;
});

And here the jsbin


If that is too simple, here is a more sophisticated example

Also see the answer here for related best practices comments

Dmitri Zaitsev
  • 13,548
  • 11
  • 76
  • 110
  • 1
    yes, I agree with you. Always try to make things simple. – Evan Hu Jan 17 '15 at 08:05
  • What is the point in declaring `var _dataObj = {};` when you returning a direct reference to it..? That is not *private*. In the first example you can do `this.dataObj = {};` and in the second `return { dataObj: {} };` it's a useless variable declaration IMHO. – T J Dec 12 '15 at 14:28
  • @TJ The point is to share this variable among other components. It is a basic example illustrating the concept of sharing. The variable IS private inside the block, then you expose it as public variable using the revealing pattern. This way there is separation of responsibilities between holding the variable and using it. – Dmitri Zaitsev Dec 12 '15 at 18:44
  • @DmitriZaitsev you say "simple examples" but unless you properly show how to make use of private state, you are just confusing people. There is no private state in your example as long as you return a direct reference . – T J Dec 12 '15 at 19:27
  • @TJ I don't see anything confusing. A private variable can be exposed by a module. Feel free to write a better answer. – Dmitri Zaitsev Dec 12 '15 at 20:05
  • @DmitriZaitsev ideally a module should be exposing methods to alter private variables in a certain way. variables in your example points to *objects*, and objects are passed by reference in javascript. If they are directly exposed, then whats the point in calling them private..? there is no privacy in the given examples, and the question doesn't ask for such private states either. The fact that you use the words *public* and *private* in such an example where there is no privacy might confuse beginners. Have a good day. – T J Dec 13 '15 at 03:18
  • @TJ Have you checked the linked example? It is all there. Check the revealing pattern - it explains what private and public means. It is purely architectural concepts, nothing to do with implementation details. You can expose your private variables directly or via accessors - the architecture stays the same, and so should the terminology. – Dmitri Zaitsev Dec 14 '15 at 13:55
26

--- I know this answer is not for this question, but I want people who reads this question and want to handle Services such as Factories to avoid trouble doing this ----

For this you will need to use a Service or a Factory.

The services are the BEST PRACTICE to share data between not nested controllers.

A very very good annotation on this topic about data sharing is how to declare objects. I was unlucky because I fell in a AngularJS trap before I read about it, and I was very frustrated. So let me help you avoid this trouble.

I read from the "ng-book: The complete book on AngularJS" that AngularJS ng-models that are created in controllers as bare-data are WRONG!

A $scope element should be created like this:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // best practice, always use a model
  $scope.someModel = {
    someValue: 'hello computer'
  });

And not like this:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // anti-pattern, bare value
  $scope.someBareValue = 'hello computer';
  };
});

This is because it is recomended(BEST PRACTICE) for the DOM(html document) to contain the calls as

<div ng-model="someModel.someValue"></div>  //NOTICE THE DOT.

This is very helpful for nested controllers if you want your child controller to be able to change an object from the parent controller....

But in your case you don't want nested scopes, but there is a similar aspect to get objects from services to the controllers.

Lets say you have your service 'Factory' and in the return space there is an objectA that contains objectB that contains objectC.

If from your controller you want to GET the objectC into your scope, is a mistake to say:

$scope.neededObjectInController = Factory.objectA.objectB.objectC;

That wont work... Instead use only one dot.

$scope.neededObjectInController = Factory.ObjectA;

Then, in the DOM you can call objectC from objectA. This is a best practice related to factories, and most important, it will help to avoid unexpected and non-catchable errors.

AFP_555
  • 2,392
  • 4
  • 25
  • 45
17

Solution without creating Service, using $rootScope:

To share properties across app Controllers you can use Angular $rootScope. This is another option to share data, putting it so that people know about it.

The preferred way to share some functionality across Controllers is Services, to read or change a global property you can use $rootscope.

var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = true;
}]);

app.controller('Ctrl2', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = false;
}]);

Using $rootScope in a template (Access properties with $root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>
Sanjeev
  • 2,607
  • 22
  • 16
  • 5
    You're using globally scoped variables at that point which deviates from the AngularJS idea of locally scoping everything within its various structures. Adding a global variable file would achieve the same thing and make it easier to find where the variable is originally defined. Either way, not suggested. – Organiccat Sep 18 '14 at 20:48
  • 4
    @Organiccat - I understand your concern and that's why i have already mentioned that the preferred way will be services, no doubt in that. But ya angular provides this way as well. It's upon you how you want to manage your global's. I had a scenario where this approach worked best for me. – Sanjeev Sep 19 '14 at 03:07
9

The sample above worked like a charm. I just did a modification just in case I need to manage multiple values. I hope this helps!

app.service('sharedProperties', function () {

    var hashtable = {};

    return {
        setValue: function (key, value) {
            hashtable[key] = value;
        },
        getValue: function (key) {
            return hashtable[key];
        }
    }
});
vusan
  • 5,221
  • 4
  • 46
  • 81
Juan Zamora
  • 386
  • 4
  • 11
  • 1
    I also created a sample using a service to share data across different controllers. I hope you guys like it. http://jsfiddle.net/juazammo/du53553a/1/ – Juan Zamora Aug 29 '14 at 20:31
  • 1
    Even though it works, this is usually the syntax for `.factory`. A `.service` should be used "if you define your service as a type/class" as per https://docs.angularjs.org/api/auto/service/$provide#service – Dmitri Zaitsev Sep 02 '14 at 12:05
  • 1
    Dmitri, you are right, however the Angular guys from my perspective, just changed a bit the concept I had between services (facades) and factories.... oh well.... – Juan Zamora Sep 09 '14 at 20:46
  • If service is for `facades`, then what is `factory` good for? – Dmitri Zaitsev Sep 10 '14 at 08:49
  • 1
    And correct me if im wrong, services are intended to return something that can be an object or a value. Factories are intended to create objects. A facace which actually is a collection of functionalities that return something, is what I thought services where. Including invoking functionalities from factories. Again, im getting into the basic notion of what this is for me and not what actually is from the Angular perspective. (Abstract Factory http://www.dofactory.com/net/abstract-factory-design-pattern) and an Adapter approach is what I will expose as a service – Juan Zamora Sep 16 '14 at 21:45
  • 1
    Check the Adapter Pattern here http://www.dofactory.com/net/adapter-design-pattern – Juan Zamora Sep 16 '14 at 21:48
6

I tend to use values, happy for anyone to discuss why this is a bad idea..

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

myApp.value('sharedProperties', {}); //set to empty object - 

Then inject the value as per a service.

Set in ctrl1:

myApp.controller('ctrl1', function DemoController(sharedProperties) {
  sharedProperties.carModel = "Galaxy";
  sharedProperties.carMake = "Ford";
});

and access from ctrl2:

myApp.controller('ctrl2', function DemoController(sharedProperties) {
  this.car = sharedProperties.carModel + sharedProperties.carMake; 

});
Chilledflame
  • 166
  • 1
  • 4
5

The following example shows how to pass variables between siblings controllers and take an action when the value changes.

Use case example: you have a filter in a sidebar that changes the content of another view.

angular.module('myApp', [])

  .factory('MyService', function() {

    // private
    var value = 0;

    // public
    return {
      
      getValue: function() {
        return value;
      },
      
      setValue: function(val) {
        value = val;
      }
      
    };
  })
  
  .controller('Ctrl1', function($scope, $rootScope, MyService) {

    $scope.update = function() {
      MyService.setValue($scope.value);
      $rootScope.$broadcast('increment-value-event');
    };
  })
  
  .controller('Ctrl2', function($scope, MyService) {

    $scope.value = MyService.getValue();

    $scope.$on('increment-value-event', function() {    
      $scope.value = MyService.getValue();
    });
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp">
  
  <h3>Controller 1 Scope</h3>
  <div ng-controller="Ctrl1">
    <input type="text" ng-model="value"/>
    <button ng-click="update()">Update</button>
  </div>
  
  <hr>
  
  <h3>Controller 2 Scope</h3>
  <div ng-controller="Ctrl2">
    Value: {{ value }}
  </div>  

</div>
Zanon
  • 29,231
  • 20
  • 113
  • 126
4

I'd like to contribute to this question by pointing out that the recommended way to share data between controllers, and even directives, is by using services (factories) as it has been already pointed out, but also I'd like to provide a working practical example of how to that should be done.

Here is the working plunker: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info

First, create your service, that will have your shared data:

app.factory('SharedService', function() {
  return {
    sharedObject: {
      value: '',
      value2: ''
    }
  };
});

Then, simply inject it on your controllers and grab the shared data on your scope:

app.controller('FirstCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.controller('SecondCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.controller('MainCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

You can also do that for your directives, it works the same way:

app.directive('myDirective',['SharedService', function(SharedService){
  return{
    restrict: 'E',
    link: function(scope){
      scope.model = SharedService.sharedObject;
    },
    template: '<div><input type="text" ng-model="model.value"/></div>'
  }
}]);

Hope this practical and clean answer can be helpful to someone.

Fedaykin
  • 4,482
  • 3
  • 22
  • 32
3

You could do that with services or factories. They are essentially the same apart for some core differences. I found this explanation on thinkster.io to be the easiest to follow. Simple, to the point and effective.

Tomasz Jakub Rup
  • 10,502
  • 7
  • 48
  • 49
Noahdecoco
  • 211
  • 2
  • 10
  • 1
    *"You could do that with services or factories"* - How..? How to do it is what the OP is asking... please post complete answer in stackoverflow itself rather than linking to external resources, links might go down overtime. – T J Dec 12 '15 at 14:21
2

Couldn't you also make the property part of the scopes parent?

$scope.$parent.property = somevalue;

I'm not saying it's right but it works.

SideFX
  • 839
  • 1
  • 12
  • 34
  • 3
    The author stated that `NOTE: These controllers are not nested inside each other.`. If these were nested controllers or controllers that shared the same parent this would work, but we can't expect that. – Chris Foster May 30 '14 at 21:29
  • 2
    It is generally a bad practice to rely on `$parent` if that can be avoided. A well-designed re-usable component should not know about its parents. – Dmitri Zaitsev Sep 02 '14 at 11:54
2

Ah, have a bit of this new stuff as another alternative. It's localstorage, and works where angular works. You're welcome. (But really, thank the guy)

https://github.com/gsklee/ngStorage

Define your defaults:

$scope.$storage = $localStorage.$default({
    prop1: 'First',
    prop2: 'Second'
});

Access the values:

$scope.prop1 = $localStorage.prop1;
$scope.prop2 = $localStorage.prop2;

Store the values

$localStorage.prop1 = $scope.prop1;
$localStorage.prop2 = $scope.prop2;

Remember to inject ngStorage in your app and $localStorage in your controller.

kJamesy
  • 5,973
  • 5
  • 20
  • 22
  • 1
    This solves a different problem - persistent storage. It is not a scalable solution for the problem in question as it makes your code leaky with side-effects such as modifying the local storage object with vulnerability of name clash among others. – Dmitri Zaitsev Dec 12 '15 at 18:55
2

There are two ways to do this

1) Use get/set service

2) $scope.$emit('key', {data: value}); //to set the value

 $rootScope.$on('key', function (event, data) {}); // to get the value
Rohan Kawade
  • 453
  • 5
  • 18
2

Second Approach :

angular.module('myApp', [])
  .controller('Ctrl1', ['$scope',
    function($scope) {

    $scope.prop1 = "First";

    $scope.clickFunction = function() {
      $scope.$broadcast('update_Ctrl2_controller', $scope.prop1);
    };
   }
])
.controller('Ctrl2', ['$scope',
    function($scope) {
      $scope.prop2 = "Second";

        $scope.$on("update_Ctrl2_controller", function(event, prop) {
        $scope.prop = prop;

        $scope.both = prop + $scope.prop2; 
    });
  }
])

Html :

<div ng-controller="Ctrl2">
  <p>{{both}}</p>
</div>

<button ng-click="clickFunction()">Click</button>

For more details see plunker :

http://plnkr.co/edit/cKVsPcfs1A1Wwlud2jtO?p=preview

Codiee
  • 3,047
  • 2
  • 17
  • 18
  • 1
    Works only if `Ctrl2` (the listener) is a child controller of `Ctrl1`. Sibling controllers have to communicate via `$rootScope`. – herzbube May 19 '18 at 16:07
2

I looked thru the answers above, I recommend pejman's Dec 29 '16 at 13:31 suggestion but he/she has not left a full answer. Here it is, I will put this as --> (you need a service and a listener $watch on one of the scopes from controllers for changes in the service area)

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

app.service('bridgeService', function () {
    var msg = ""; 
    return msg;
});
app.controller('CTRL_1'
, function ($scope, $http, bridgeService) 
{
    $http.get(_restApi, config)
    .success(
    function (serverdata, status, config) {
        $scope.scope1Box = bridgeService.msg = serverdata;
    });
});
app.controller('CTRL_2'
, function ($scope, $http, bridgeService) 
{
    $scope.$watch( function () {
        return (bridgeService.msg);
    }, function (newVal, oldVal) {
        $scope.scope2Box = newVal;
    }, true
    );
});
Jenna Leaf
  • 2,255
  • 21
  • 29
0

If you don't want to make service then you can do like this.

var scope = angular.element("#another ctrl scope element id.").scope();
scope.plean_assign = some_value;
thanksnote
  • 1,012
  • 11
  • 9
  • 37
    I don't doubt this answer works, but I want to note this goes against AngularJS's philosophy to never have DOM objects in your model/controller code. – JoeCool Oct 03 '13 at 14:42
  • 3
    -1 because controller communication via the DOM is poor practice, in my opinion. – Chris Foster May 30 '14 at 21:31
  • 3
    @ChrisFoster, just because a hammer is sold as a "tool", it doesn't mean it can't be used as a paper weight. I'm sure that for every framework or tool out there you'll always find developers which need to "bend" the "best practices" list. – Andrei V Aug 22 '14 at 13:06
  • 5
    @AndreiV - Poor analogy, there is no disadvantage to using a hammer as a paper weight. Doing bad practice like this has clear disadvantages and can easily lead to spaghetti code. The code above is fragile because it now depends on where your controller is in the DOM and is very difficult to test. Using a service is better practice for a reason, because it does not tie your implementation to your template. I agree developers often need to bend the best practices list, but not when there is a clear, common, more modular best practice that works better. – Chris Foster Aug 22 '14 at 17:49
-1

Besides $rootScope and services, there is a clean and easy alternative solution to extend angular to add the shared data:

in the controllers:

angular.sharedProperties = angular.sharedProperties 
    || angular.extend(the-properties-objects);

This properties belong to 'angular' object, separated from the scopes, and can be shared in scopes and services.

1 benefit of it that you don't have to inject the object: they are accessible anywhere immediately after your defination!

isherwood
  • 58,414
  • 16
  • 114
  • 157
williamjxj
  • 1
  • 1
  • 3
  • 2
    This is like having global variables all over `window` object... If you are going to *pollute* angular, why not just go ahead and *pollute* the window object... – T J Dec 12 '15 at 14:33