Here's a situation where a dot is needed.
When you have a $scope value that you want to use as a ngModel value (let's call it selectedItem), you might be tempted to just create $scope.selectedItem and pass that to the template. However, this is dangerous if the template creates child scopes.
There are certain AngularJS directives that create child scopes:
ngRepeat
ngIf
ngController
- ...and others (their doc page will say "This directive creates new scope" under the Usage heading).
Child scopes are dangerous because of how scope inheritance works. The relationship is this:
-- $parent scope
├ selectedItem
└─ $child scope
As a child scope, the $child object prototypically inherits from $parent. That's a javascript term that basically means you can get any $parent property by getting $child.<property>. But you cannot set values, which is the problem. This is just how javascript works.
So a template can access $parent.selectedItem by reading $child.selectedItem. But if the template sets $child.selectedItem, it sets it on $child not $parent, so now you have two versions of selectedItem:
-- $parent scope
├ selectedItem
└─ $child scope
└ selectedItem
And ngModel directives both get and set the scope value. The getting works, but the setting breaks things (ss others have explained).
Why using a dot solves the problem
When you store the selectedItem value with a dot on the parent scope (e.g. $scope.vm.selectedItem, then the child scope template only ever gets the vm object.
Using vm, the relationship looks like this:
-- $parent scope
├ selectedItem
│ └─ vm
│ └─ selectedItem
└─ $child scope
The $child scope only ever reads the vm object; it never writes a new vm object (in JS, objects are references not values). And prototypical inheritance is only involved in accessing vm. After a scope gets the vm object, it can directly use its values without any prototypical inheritance.
Think of it as passing around an object between scopes.