6

In a parent controller scope, I have defined selectedItem which is set to 'x'. Then in the child scope, I have defined selectedItem using ngModel:

<div ng-app>
  <div ng-controller="CtrlA">
       <div ng-controller="CtrlB">
         <select ng-model="selectedItem" ng-options="item for item in items">
         </select>
      </div>
  </div>
</div>

function CtrlA($scope) {
    $scope.selectedItem = 'x';
    $scope.items = ['x', 'y'];
}

function CtrlB($scope) {}

When the page is loaded, the selectedItem is properly set to 'x' as expected. When I select 'y', selectedItem in CtrlB $scope gives 'y' as expected.

But when I inspect $scope.selectedItem in CtrlA scope (using AngularJS's batarang), it gives 'x' .

jsFiddle: http://jsfiddle.net/sudhh/GGKjp/2/

Preview page: http://fiddle.jshell.net/sudhh/GGKjp/2/show/light/ (for inspecting with angularjs batarang)

Why is $scope.selectedItem in CtrlA scope not getting updated to 'y'? What is the explanation?

I prefer not to use events or rootScope to update selectedItem in parent scope (for learning purposes).

1
  • 1
    Be sure to read Stack Overflow question stackoverflow.com/questions/14049480 It gives a good in-depth overview of scope inheritance in AngularJS. Commented Jul 15, 2013 at 7:19

2 Answers 2

7

If you try to bind to a primitive declared on parent scope, then the selectedItem in child scope will effectively shadow the property of the same name in the parent scope.

In this case there are 3 choices

  1. define objects in the parent for your model, then reference a property of that object in the child: ref.selectedItem
  2. use $parent.selectedItem (not always possible, but easier than 1. where possible)
  3. define a function on the parent scope, and call it from the child, passing the primitive value up to the parent (not always possible)

More about it on https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance

You can find the updated fiddle using the first approach at http://jsfiddle.net/sudhh/XU2rP/1/

function CtrlA($scope) {
  $scope.items = ['x', 'y'];
  $scope.ref = {
    selectedItem: 'x'
  };
}
Sign up to request clarification or add additional context in comments.

1 Comment

Indeed, I extended your code a bit in this jsFiddle by using watches which log to console. Only the watch in B gets triggered.
0

I've noticed in similar cases that AngularJS does not watch selectedItem properly. The only way I found is to initialize selectedItem with an entry from the items array. Try the following:

function CtrlA($scope) {
    $scope.items = ['x', 'y'];
    $scope.selectedItem = $scope.items[0];
}

2 Comments

Still doesnt work. If you select "y" from the dropdown & inspect the scope, CtrlB gives 'y' as expected but CtrlA still shows selectedItem as 'x'
Angular is watching selectedItem properly. The issue is that there are two selectedItem properties -- one in the parent scope and one in the child scope. Angular is watching both of them, properly, but independently. Because of the way JavaScript prototypal inheritance works, the child scope will only see its property, and the parent only its property. What you want is to have the child scope use the property that already exists in the parent scope, and not create a new property. (I did see that @sudhakar already found the answer, I just wanted to clarify what is going on.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.