16

I think I'm missing something simple (and important) here. I'm using an included template that contains an input that's mapped to some value:

<div ng-controller="Ctrl">
    <div ng-include src="'template.html'"></div>
</div>

<!-- template -->
<script type="text/ng-template" id="template.html">
    <input ng-model="testvalue" />
</script>

Controller:

function Ctrl($scope) {    
   $scope.testvalue= "initial value";
}​

Alerting the value of $scope.testvalue always shows the initial value, not the updated value (when you type in the input). Help me Obi-Wan. You're our only hope.

Fiddle: http://jsfiddle.net/h5aac/

Caleb
  • 2,303
  • 1
  • 20
  • 21

2 Answers2

31

This is the all too common of binding to a primitive instead of an object. The value of the string gets passed around and not a reference to an object. If you use an object instead of a primitive, it works fine. Something like this in your scope.

$scope.foo = {testvalue: "initial value"};

See http://jsfiddle.net/h5aac/2/

Also:

Using `ng-model` within a transcluded directive in AngularJS

binding issue when a directive in a ngRepeat

AngularJS - updating scope value with asynchronous response

I'm sure there are more...

Community
  • 1
  • 1
dnc253
  • 39,967
  • 41
  • 141
  • 157
  • Thanks, that was exactly it. Is this a "bug" in angular, or is this intentional/expected? – Caleb Dec 12 '12 at 21:06
  • 4
    I wouldn't really call it a "bug". It's just a matter of how javascript objects and primitives work when they get passed around. If you a var that is set to a string and you pass it into a function, the function can change it all it wants, and it's not going to affect the value of the original var. If that var was set to an object, then it's a reference that's getting passed in, and any changes made to it in the function will affect the object that the original var was referencing. Hope this makes sense. – dnc253 Dec 12 '12 at 22:04
  • Yes, that makes perfect sense. Thank you! – Caleb Dec 12 '12 at 22:44
7

An alternative to referencing an object property in the parent scope is to use $parent to access the primitive in the parent scope:

<input ng-model="$parent.testvalue" />

ng-include creates a child scope. That scope prototypically inherits from Ctrl's parent scope. Here's how the 3 variations work:

  • $parent.testvalue ties the model to the property in the parent scope
  • testvalue by itself ties the model to a new property that will be created on the child scope. This property "shadows/hides" the parent scope property by the same name.
  • foo.testvalue (e.g., see @dnc253's answer) also ties the model to a parent property. It works like this: Javascript doesn't see/find 'foo' in the child scope, so it looks for it in the parent scope (due to prototypical inheritance) and finds it there.

To see what the child scope looks like, use your original fiddle, and add this code to your template somewhere:

<a ng-click="showScope($event)">show scope</a>

And add this code to your Ctrl:

$scope.showScope = function(e) {
   console.log(angular.element(e.srcElement).scope());
}

Before you type into the textbox, click the "show scope" link. In the console (I'm using Chrome), you can expand the "Child" scope and see it does not contain a testvalue property yet (which I find surprising, because I don't know how it is displaying the "initial value" in the textbox). You can expand the $parent and you'll see the testvalue property there -- a property with this name appears to only be in the parent scope at this point.

Now, clear the console, type into the textbox, and click the "show scope" link again. You'll see that the "Child" scope now has a new testvalue property. It shadows/hides the parent property. So, things in the child scope see the child scope testvalue property, and things in the parent scope see the parent scope testvalue property.

Update: FYI, I recently wrote an extensive answer/article about scope prototypical inheritance that explains the above concepts in much more detail, with lots of pictures: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492