Passing data between controllers in Angular JS?

I have a basic controller that displays my products,

App.controller('ProductCtrl',function($scope,$productFactory){
     $productFactory.get().success(function(data){
           $scope.products = data;
     });
});

In my view I'm displaying this products in a list

<ul>
    <li ng-repeat="product as products">
        {{product.name}}
    </li>
</ul

What I'm trying to do is when someone click on the product name, i have another view named cart where this product is added.

 <ul class="cart">
      <li>
          //click one added here
      </li>
      <li>
          //click two added here
      </li>
 </ul>

So my doubt here is, how do pass this clicked products from first controller to second? i assumed that cart should be a controller too.

I handle click event using directive. Also i feel i should be using service to achieve above functionality just can't figure how? because cart will be predefined number of products added could be 5/10 depending on which page user is. So i would like to keep this generic.

Update:

I created a service to broadcast and in the second controller i receive it. Now the query is how do i update dom? Since my list to drop product is pretty hardcoded.


From the description, seems as though you should be using a service. Check out http://egghead.io/lessons/angularjs-sharing-data-between-controllers and AngularJS Service Passing Data Between Controllers to see some examples.

You could define your product service (as a factory) as such:

app.factory('productService', function() {
  var productList = [];

  var addProduct = function(newObj) {
      productList.push(newObj);
  };

  var getProducts = function(){
      return productList;
  };

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

Dependency inject the service into both controllers.

In your ProductController, define some action that adds the selected object to the array:

app.controller('ProductController', function($scope, productService) {
    $scope.callToAddToProductList = function(currObj){
        productService.addProduct(currObj);
    };
});

In your CartController, get the products from the service:

app.controller('CartController', function($scope, productService) {
    $scope.products = productService.getProducts();
});

how do pass this clicked products from first controller to second?

On click you can call method that invokes broadcast:

$rootScope.$broadcast('SOME_TAG', 'your value');

and the second controller will listen on this tag like:

$scope.$on('SOME_TAG', function(response) {
      // ....
})

Since we can't inject $scope into services, there is nothing like a singleton $scope.

But we can inject $rootScope. So if you store value into the Service, you can run $rootScope.$broadcast('SOME_TAG', 'your value'); in the Service body. (See @Charx description about services)

app.service('productService',  function($rootScope) {/*....*/}

Please check good article about $broadcast, $emit


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>

You can do this by two methods.

  1. By using $rootscope, but I don't reccommend this. The $rootScope is the top-most scope. An app can have only one $rootScope which will be shared among all the components of an app. Hence it acts like a global variable.

  2. Using services. You can do this by sharing a service between two controllers. Code for service may look like this:

    app.service('shareDataService', function() {
        var myList = [];
    
        var addList = function(newObj) {
            myList.push(newObj);
        }
    
        var getList = function(){
            return myList;
        }
    
        return {
            addList: addList,
            getList: getList
        };
    });
    

    You can see my fiddle here.


An even simpler way to share the data between controllers is using nested data structures. Instead of, for example

$scope.customer = {};

we can use

$scope.data = { customer: {} };

The data property will be inherited from parent scope so we can overwrite its fields, keeping the access from other controllers.