47

How can I take variable changes and easily propagate them back to the ParentCtrl despite a 'new' var being instantiated in the ChildCtrl? Extra points for minimal to no $on's and $watch's (makes it easier to implement)

ParentCtrl

  • ChildCtrl / ChildCtrl2 / ChildCtrl3 / ChildCtrl4

    • View

My ChildCtrl's are just different enough where I can't easily abstract a master layout and a ng-view, but they all depend on the same functions in ParentCtrl.

$scope.searchTerms is defined in ParentCtrl but the input box with ng-model='searchTerms' is in the view of the child controllers. When this var changes it's not reflected in the ParentCtrl only the ChildCtrls.

Example: http://jsfiddle.net/JHwxP/22/

HTML Partial

<div ng-app>
    <div ng-controller="Parent">
        parentX = {{x}} <br/>
        parentY = {{y}}<br/>
        <div ng-controller="Child">
            childX = {{x}}<br/>
            childY = {{y}}<br/>
            <a ng-click="modifyBothScopes()">modifyBothScopes</a><br>
            <br>
            The changes here need to appear in 'Parent'. <input ng-model='y'>
        </div>
    </div>
</div>

Controllers

function Parent($scope) {
    $scope.x= 5;
    $scope.y= 5;
}

function Child($scope) {
    $scope.modifyBothScopes= function() {
       $scope.$parent.x++;
    };  
}

UPDATE

I'm currently attempting a shared service approach: https://gist.github.com/exclsr/3595424

UPDATE

Trying an emit/broadcast system

SOLVED Problem: I was storing $scope.searchTerms in the parent and when changed created a space in the child $scope.

Solution: I should have done $scope.search.terms in the parent and when changed in the child it would bubble up to the parent.

Example: http://jsfiddle.net/JHwxP/23/

JJJ
  • 32,902
  • 20
  • 89
  • 102
Michael J. Calkins
  • 32,082
  • 15
  • 62
  • 91

3 Answers3

88

This is due to how prototypal inheritance works.

When you ask for $scope.x in the child controller, it checks to see if x is defined on its scope, and if not, looks for x in the parent scope.

If you assign to the child scope's x property, it only modifies the child scope.

An easy way to deal with this and get the sharing behavior is to use an object or an array.

function ParentCtrl($scope) {
  $scope.model = {x: 5, y: 5};
}

function ChildCtrl($scope) {
  $scope.update = function(x, y) {
    $scope.model.x = x;
    $scope.model.y = y;
  };
}

Here, the changes will be visible in both scopes, because $scope.model will refer to the same object in both parent and child scopes.

John Lindquist has a video on this.

satchmorun
  • 12,487
  • 2
  • 41
  • 27
  • 9
    I was aware of Misko's "If you're not using a dot, you're doing something wrong." talk but I had never experienced why. Here's a working example of your answer: http://jsfiddle.net/JHwxP/23/ – Michael J. Calkins Jun 06 '13 at 22:46
15

Another solution that doesn't involve creating a bunch of objects to pass by reference is to create setter functions in the parent controller.

function ParentCtrl($scope) {
  $scope.x = 5;
  $scope.y = 5;

  $scope.setX = function(value) {
    $scope.x = value;
  }

  $scope.setY = function(value) {
    $scope.y = value;
  }
}

function ChildCtrl($scope) {
  $scope.update = function(x, y) {
    $scope.setX(x);
    $scope.setY(y);
  };
}

I find this to be cleaner when it may not make sense to have the data be part of the same object.

You could also go crazy and do something like this in the child:

function ChildCtrl($scope) {
  var superSetX = $scope.setX;
  $scope.setX = function(value) {
    superSetX(value * 2);
  };
  ...
}
rtcherry
  • 4,840
  • 22
  • 27
1

I was looking for something like this. The first solution came after reading this. You can share a parent model with a child controller.

Here is an example. I am even using an array of items to prove my point. The ChildController is changing his parent, like they were all part of the same universe.

genuinefafa
  • 703
  • 1
  • 6
  • 21