It's not about how AngularJS works - it's about how JavaScript works, actually. Consider the following:
function Parent() {
this.message = '';
this.data = {};
}
function Child() {}; Child.prototype = new Parent();
As you see, Parent
, when used as a constructor, defines two properties for a fresh object. Let's say we're going to use the first one to store a primitive, and the second one to store an object.
Child
function is defined really simple: no properties of its own, having Parent
object in its prototype chain. Now that's do some more things with them:
var c = new Child();
c.message = '42';
c.data.message = '42';
console.log(c.hasOwnProperty('message')); // true
console.log(c.hasOwnProperty('data')); // false
console.log(c.data.hasOwnProperty('message')); // true
See the difference? In the first case, with c.message
, we have defined a new property on a child object (c
) itself - and that one now shadows the same-named property defined on its prototype.
In the second case, however, we are still using the property of parent, extending its value with a new property. No new properties are defined on c
, only its prototype is affected; the difference is even more telling here:
var d = new Child();
d.message = '34';
d.data.message = '34';
console.log(c.message); // still '42'
console.log(c.data.message); // now '34'
Similar things happen when you use Angular directive: a new scope is defined, inheriting all the 'model' properties from its parent scope. But unless these properties are 'two-level', assigning to them will introduce the same-named property on a child level, shadowing the one defined for the parent.
I'd suggest studying Understanding Scopes document from Angular wiki: it has much more substantial (and richly illustrated, too!) explanation for this behavior. This quote, however, summarizes it pretty well:
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models.
And it has some workarounds mentioned for the cases when one indeed wants to work with primitives only: $parent.parentScopeProperty
is the most straight-forward one, I suppose.