AngularJS: How can I pass variables between controllers?

JavascriptAngularjsAngularjs Controller

Javascript Problem Overview


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.

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

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;
});

.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;
});

.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

Solution 3 - Javascript

--- 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.

Solution 4 - Javascript

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>

Solution 5 - Javascript

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];
        }
    }
});

Solution 6 - Javascript

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; 
  
});

Solution 7 - Javascript

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>

Solution 8 - Javascript

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.

Solution 9 - Javascript

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.

Solution 10 - Javascript

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.

Solution 11 - Javascript

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.

Solution 12 - Javascript

There are two ways to do this

  1. Use get/set service

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

 $rootScope.$on('key', function (event, data) {}); // to get the value

Solution 13 - Javascript

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

Solution 14 - Javascript

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
    );
});

Solution 15 - Javascript

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;
   

Solution 16 - Javascript

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!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondopatramanView Question on Stackoverflow
Solution 1 - JavascriptGloopyView Answer on Stackoverflow
Solution 2 - JavascriptDmitri ZaitsevView Answer on Stackoverflow
Solution 3 - JavascriptAFP_555View Answer on Stackoverflow
Solution 4 - JavascriptSanjeevView Answer on Stackoverflow
Solution 5 - JavascriptJuan ZamoraView Answer on Stackoverflow
Solution 6 - JavascriptChilledflameView Answer on Stackoverflow
Solution 7 - JavascriptZanonView Answer on Stackoverflow
Solution 8 - JavascriptFedaykinView Answer on Stackoverflow
Solution 9 - JavascriptNoahdecocoView Answer on Stackoverflow
Solution 10 - JavascriptSideFXView Answer on Stackoverflow
Solution 11 - JavascriptkJamesyView Answer on Stackoverflow
Solution 12 - JavascriptRohan KawadeView Answer on Stackoverflow
Solution 13 - JavascriptCodieeView Answer on Stackoverflow
Solution 14 - JavascriptJenna LeafView Answer on Stackoverflow
Solution 15 - JavascriptthanksnoteView Answer on Stackoverflow
Solution 16 - JavascriptwilliamjxjView Answer on Stackoverflow