In the "Create Components" section of AngularJS's homepage, there is this example:
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
}
Notice how the select
method is added to $scope
, but the addPane
method is added to this
. If I change it to $scope.addPane
, the code breaks.
The documentation says that there in fact is a difference, but it doesn't mention what the difference is:
Previous versions of Angular (pre 1.0 RC) allowed you to use this interchangeably with the $scope method, but this is no longer the case. Inside of methods defined on the scope this and $scope are interchangeable (angular sets this to $scope), but not otherwise inside your controller constructor.
How does this
and $scope
work in AngularJS controllers?
"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.
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.
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:
https://i.stack.imgur.com/PUMuU.png
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.
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.
The reason 'addPane' is assigned to this is because of the <pane>
directive.
The pane
directive does require: '^tabs'
, which puts the tabs controller object from a parent directive, into the link function.
addPane
is assigned to this
so that the pane
link function can see it. Then in the pane
link function, addPane
is just a property of the tabs
controller, and it's just tabsControllerObject.addPane. So the pane directive's linking function can access the tabs controller object and therefore access the addPane method.
I hope my explanation is clear enough.. it's kind of hard to explain.
I just read a pretty interesting explanation on the difference between the two, and a growing preference to attach models to the controller and alias the controller to bind models to the view. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ is the article.
NOTE: The original link still exists, but changes in formatting have made it hard to read. It's easier to view in the original.
He doesn't mention it but when defining directives, if you need to share something between multiple directives and don't want a service (there are legitimate cases where services are a hassle) then attach the data to the parent directive's controller.
The $scope
service provides plenty of useful things, $watch
being the most obvious, but if all you need to bind data to the view, using the plain controller and 'controller as' in the template is fine and arguably preferable.
I recommend you to read the following post: AngularJS: "Controller as" or "$scope"?
It describes very well the advantages of using "Controller as" to expose variables over "$scope".
I know you asked specifically about methods and not variables, but I think that it's better to stick to one technique and be consistent with it.
So for my opinion, because of the variables issue discussed in the post, it's better to just use the "Controller as" technique and also apply it to the methods.
In this course(https://www.codeschool.com/courses/shaping-up-with-angular-js) they explain how to use "this" and many other stuff.
If you add method to the controller through "this" method, you have to call it in the view with controller's name "dot" your property or method.
For example using your controller in the view you may have code like this:
<div data-ng-controller="YourController as aliasOfYourController">
Your first pane is {{aliasOfYourController.panes[0]}}
</div>
$scope
, so thanks for mentioning it.
as
and this
so how can it help explain the difference?
$scope
was never referred, I learned to use just this
in the controllers. The problem is that when you start to have tohandle promises in your controller, you have a lot of references problem to this
and have to start doing things like var me = this
to reference the model in this
from within the promise return function. So because of that, I'm still very confused about which method should me used, $scope
or this
.
var me = this
or .bind(this)
whenever you do Promises, or other closure-heavy stuff. It has nothing to do with Angular.
ng-controller="MyCtrl as MC"
is equivalent to putting $scope.MC = this
in the controller itself -- it defines an instance (this) of MyCtrl on the scope for use in the template via {{ MC.foo }}
Previous versions of Angular (pre 1.0 RC) allowed you to use this interchangeably with the $scope method, but this is no longer the case. Inside of methods defined on the scope this and $scope are interchangeable (angular sets this to $scope), but not otherwise inside your controller constructor.
To bring back this behaviour (does anyone know why was it changed?) you can add:
return angular.extend($scope, this);
at the end of your controller function (provided that $scope was injected to this controller function).
This has a nice effect of having access to parent scope via controller object that you can get in child with require: '^myParentDirective'
$scope has a different 'this' then the controller 'this'.Thus if you put a console.log(this) inside controller it gives you a object(controller) and this.addPane() adds addPane Method to the controller Object. But the $scope has different scope and all method in its scope need to be accesed by $scope.methodName(). this.methodName()
inside controller means to add methos inside controller object.$scope.functionName()
is in HTML and inside
$scope.functionName(){
this.name="Name";
//or
$scope.myname="myname"//are same}
Paste this code in your editor and open console to see...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
<script>
var app=angular.module("myApp",[]);
app.controller("ctrlExample",function($scope){
console.log("ctrl 'this'",this);
//this(object) of controller different then $scope
$scope.firstName="Andy";
$scope.lastName="Bot";
this.nickName="ABot";
this.controllerMethod=function(){
console.log("controllerMethod ",this);
}
$scope.show=function(){
console.log("$scope 'this",this);
//this of $scope
$scope.message="Welcome User";
}
});
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
Comming From $SCOPE :{{firstName}}
<br><br>
Comming from $SCOPE:{{lastName}}
<br><br>
Should Come From Controller:{{nickName}}
<p>
Blank nickName is because nickName is attached to
'this' of controller.
</p>
<br><br>
<button ng-click="controllerMethod()">Controller Method</button>
<br><br>
<button ng-click="show()">Show</button>
<p>{{message}}</p>
</div>
</body>
</html>
Success story sharing