"How does this
and $scope
work in AngularJS controllers?"
Short answer:
this
- When the controller constructor function is called,
this
is the controller.
- When a function defined on a
$scope
object is called, this
is the "scope in effect when the function was called". This may (or may not!) be the $scope
that the function is defined on. So, inside the function, this
and $scope
may not be the same.
$scope
- Every controller has an associated
$scope
object.
- A controller (constructor) function is responsible for setting model properties and functions/behaviour on its associated
$scope
.
- Only methods defined on this
$scope
object (and parent scope objects, if prototypical inheritance is in play) are accessible from the HTML/view. E.g., from ng-click
, filters, etc.
Long answer:
A controller function is a JavaScript constructor function. When the constructor function executes (e.g., when a view loads), this
(i.e., the "function context") is set to the controller object. So in the "tabs" controller constructor function, when the addPane function is created
this.addPane = function(pane) { ... }
it is created on the controller object, not on $scope. Views cannot see the addPane function -- they only have access to functions defined on $scope. In other words, in the HTML, this won't work:
<a ng-click="addPane(newPane)">won't work</a>
After the "tabs" controller constructor function executes, we have the following:
The dashed black line indicates prototypal inheritance -- an isolate scope prototypically inherits from Scope. (It does not prototypically inherit from the scope in effect where the directive was encountered in the HTML.)
Now, the pane directive's link function wants to communicate with the tabs directive (which really means it needs to affect the tabs isolate $scope in some way). Events could be used, but another mechanism is to have the pane directive require
the tabs controller. (There appears to be no mechanism for the pane directive to require
the tabs $scope.)
So, this begs the question: if we only have access to the tabs controller, how do we get access to the tabs isolate $scope (which is what we really want)?
Well, the red dotted line is the answer. The addPane() function's "scope" (I'm referring to JavaScript's function scope/closures here) gives the function access to the tabs isolate $scope. I.e., addPane() has access to the "tabs IsolateScope" in the diagram above because of a closure that was created when addPane() was defined. (If we instead defined addPane() on the tabs $scope object, the pane directive would not have access to this function, and hence it would have no way to communicate with the tabs $scope.)
To answer the other part of your question: how does $scope work in controllers?
:
Within functions defined on $scope, this
is set to "the $scope in effect where/when the function was called". Suppose we have the following HTML:
<div ng-controller="ParentCtrl">
<a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
<div ng-controller="ChildCtrl">
<a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
</div>
</div>
And the ParentCtrl
(Solely) has
$scope.logThisAndScope = function() {
console.log(this, $scope)
}
Clicking the first link will show that this
and $scope
are the same, since "the scope in effect when the function was called" is the scope associated with the ParentCtrl
.
Clicking the second link will reveal this
and $scope
are not the same, since "the scope in effect when the function was called" is the scope associated with the ChildCtrl
. So here, this
is set to ChildCtrl
's $scope
. Inside the method, $scope
is still the ParentCtrl
's $scope.
Fiddle
I try to not use this
inside of a function defined on $scope, as it becomes confusing which $scope is being affected, especially considering that ng-repeat, ng-include, ng-switch, and directives can all create their own child scopes.
Best Answer
Compile :
This is the phase where Angular actually compiles your directive. This compile function is called just once for each references to the given directive. For example, say you are using the ng-repeat directive. ng-repeat will have to look up the element it is attached to, extract the html fragment that it is attached to and create a template function.
If you have used HandleBars, underscore templates or equivalent, its like compiling their templates to extract out a template function. To this template function you pass data and the return value of that function is the html with the data in the right places.
The compilation phase is that step in Angular which returns the template function. This template function in angular is called the linking function.
Linking phase :
The linking phase is where you attach the data ( $scope ) to the linking function and it should return you the linked html. Since the directive also specifies where this html goes or what it changes, it is already good to go. This is the function where you want to make changes to the linked html, i.e the html that already has the data attached to it. In angular if you write code in the linking function its generally the post-link function (by default). It is kind of a callback that gets called after the linking function has linked the data with the template.
Controller :
The controller is a place where you put in some directive specific logic. This logic can go into the linking function as well, but then you would have to put that logic on the scope to make it "shareable". The problem with that is that you would then be corrupting the scope with your directives stuff which is not really something that is expected. So what is the alternative if two Directives want to talk to each other / co-operate with each other? Ofcourse you could put all that logic into a service and then make both these directives depend on that service but that just brings in one more dependency. The alternative is to provide a Controller for this scope ( usually isolate scope ? ) and then this controller is injected into another directive when that directive "requires" the other one. See tabs and panes on the first page of angularjs.org for an example.