1

I'm writing my first app using AngularJS, and I'm stuck at some point where I need to share data across different controllers.

I have something like this :

function ctrl1 ($scope) {
  $scope.data = new Object();
}

function ctrl2 ($scope, $http) {
  $http.get('my_page').success(function(html) {
    // I set some values to the parent's data object
    $scope.data['my_key'] = 'My value';
    // The html loaded here contains a ng-controller='ctrl3' somewhere
    $('#mydiv').html(html);
    // So I bootstrap it
    angular.bootstrap($('#mydiv'), ['my_module']);
  });
}

// Is not a child of ctrl2, but is a child of ctrl1
function ctrl3 ($scope) {
  $scope.my_key = $scope.data.my_key; // Cannot read property 'my_key' of undefined 
  // And in an ng-repeat where I want to display my_key, nothing is shown. 
  // However, if I manually set my_key here, everything is displayed correctly.
  // So the controller and ng-repeat are correctly parsed after bootstrapping
}

Here is the HTML :

<div ng-controller="ctrl1">
  <div ng-controller="ctrl2">
    <!-- some content -->
  </div>
  <!-- some more code -->
  <div id="myDiv">
    <!-- currently empty, will be added html with AJAX, ang ng-controller="ctrl3" is in this (as well as my ng-repeat) -->
  </div>
</div>

According to this very nice answer, I should be able to read and set properties of data if it's not set in the child scope and is set in the parent scope.

Why isn't this working?

[EDIT]

Figured it out now, here's the solution. I had to inject $compile into my ctrl2 and compile the code with it before adding it to the DOM.

function ctrl2 ($scope, $http, $compile) {
  $http.get('my_page').success(function(html) {
    // I set some values to the parent's data object
    $scope.data['my_key'] = 'My value';
    // The html loaded here contains a ng-controller='ctrl3' somewhere
    // ** Have to compile it before appending to the DOM
    $('#mydiv').html($compile(html)($scope));
  });
}
Community
  • 1
  • 1
Samuel Bolduc
  • 18,163
  • 7
  • 34
  • 55

2 Answers2

2

The issue you're seeing here isn't scope inheritance, the problem is your call to angular.bootstrap:

angular.bootstrap($('#myDiv'), ['my_module']);

This is telling angular that the root DOM element of the app is the #myDiv, so your ng-controller="ctrl1" and ng-controller="ctrl2" aren't considered as part of this app. Therefore you don't get expected result.

Fix:

<div id="myContainer" ng-controller="ctrl1">
  <div ng-controller="ctrl2">
    <!-- some content -->
  </div>
  <!-- some more code -->
  <div id="myDiv">
    <!-- currently empty, will be added html with AJAX, ang ng-controller="ctrl3" is in this (as well as my ng-repeat) -->
  </div>
</div>

and in JS:

angular.bootstrap($('#myContainer'), ['my_module']);
Ye Liu
  • 8,946
  • 1
  • 38
  • 34
  • That's not the issue here. He'd still have no way to pass data between controller scopes. – Christopher Marshall Aug 06 '13 at 17:43
  • ctrl2's scope is a child of ctrl1's scope, **scope inheritance** works. Read the other SO answer linked in PO's question if you're interested in this topic. – Ye Liu Aug 06 '13 at 17:51
  • Looks like you put the finger on the problem! I added an `ng-init` along with `ng-controller="ctrl3"`. Looks like it parses it when I call `bootstrap` on my root, but now I have two problems. 1. It calls all the other `ng-init` again, which is pretty bad for me right now, and 2. It seems any key added to data from ctrl2 is not available from ctrl3... – Samuel Bolduc Aug 06 '13 at 18:12
  • Please update your original question with those progress, new code, new html templates, and ask more questions from there – Ye Liu Aug 06 '13 at 19:06
  • I've figured it out, and the problem was not about the inheritance not working, but more about the way I added the newly created code to Angular. I edited my first post to show the solution. However your solution was the closest to being the right one! – Samuel Bolduc Aug 07 '13 at 13:55
1

You can either build some sort of service helper, or you can use the built in $on and $broadcast to pass data between scopes.

e.g. Set & pass data

$scope.functionName = function () {
 $scope.message = "This is a message, string, object or whatever";
 $rootScope.$broadcast('message', message);
};

Catch broadcast in another controller

    $scope.$on('message', function(message){
     $scope.message = message;

     // You now have access to message 
   });

There's a more elegant way of doing this so you don't have to pollute $rootScope, but I can't remember how to do it right now.

Added example. http://jsfiddle.net/vPq2P/3/

Christopher Marshall
  • 10,678
  • 10
  • 55
  • 94
  • This is right. Though using the service helper might be a cleaner way to do it. Create a service module, and you can inject any variables defined within the module into your controllers. – Zack Argyle Aug 06 '13 at 15:56