I am trying to learn angularjs version 1.x using this
instead of $scope
, by doing var vm = this;
. In general it works fine, but sometimes, with some functions, I am forced to use $scope
for my code to work.
For this I use thinkster 's tutorial : Building Web Applications with Django and AngularJS and SitePoint 's book , AngularJS: From Newbie to Ninja .
When trying to adapt the code from the book to that used in the thinkster tutorial , I find that I can't always use the variable vm
, for example:
In the book the code for an example function is:
angular.module('myApp.controllers', []);
// código eliminado para ahorrar espacio
angular.module('myApp.controllers').controller('StatsController', function($scope) {
$scope.name = 'World';
$scope.status = 'Connected';
$scope.statusColor='green';
$scope.$on('EVENT_NO_DATA', function(event, data) {
console.log('received broadcasted event');
$scope.status = data;
$scope.statusColor='red';
$scope.$emit('EVENT_RECEIVED');
}); });
I do the adaptation of this code ( according to my way of understanding it), like this:
(function(){
'use strict';
angular.module('myEmit', [
'myEmit.controllers',
]);
angular.module('myEmit.controllers', [])
.controller('MessageController', MessageController)
.controller('StatController', StatController);
function MessageController($scope, $timeout) {
var vm = this;
vm.messages = [{
sender: 'user1',
text: 'Mensaje 1'
}];
var timer;
var count = 0;
vm.loadMessages = function(){
count++;
vm.messages.push({
sender: 'user1',
text: 'Mensaje aleatorio ' + count
});
timer = $timeout(vm.loadMessages, 2000);
if(count == 3){
$scope.$broadcast('EVENT_NO_DATA', 'Not Connected');
$timeout.cancel(timer);
}
};
timer = $timeout(vm.loadMessages, 2000);
$scope.$on('EVENT_RECEIVED', function(){
console.log('Received emitted event');
});
};
function StatController($scope) {
var vm = this;
vm.name = 'World';
vm.status = 'Connected';
vm.statusColor = 'green';
$scope.$on('EVENT_NO_DATA', function(event, data){
console.log('Received broadcast event');
vm.status = data;
vm.statusColor = 'red';
$scope.$emit('EVENT_RECEIVED');
});
};
})();
<!DOCTYPE html>
<html ng-app='myEmit'>
<head>
<meta charset="utf-8">
<title>AngularJS Events</title>
</head>
<body ng-controller="MessageController as mc">
<h4>Mensajes:</h4>
<ul>
<li ng-repeat="mensaje in mc.messages">
{{ mensaje.text }}
</li>
</ul>
<div class="" ng-controller="StatController as sc">
<h4>Estadísticas: </h4>
<div class="">
{{ mc.messages.length}} Mensaje(s)
</div>
<div class="">
Estado: <span style="color: {{ sc.statusColor}}">
{{ sc.status }}
</span>
</div>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</html>
Problem
When I try to use this code,
function StatController($scope) {
var vm = this;
vm.name = 'World';
vm.status = 'Connected';
vm.statusColor = 'green';
vm.$on('EVENT_NO_DATA', function(event, data){
console.log('Received broadcast event');
vm.status = data;
vm.statusColor = 'red';
$scope.$emit('EVENT_RECEIVED');
});
};
the controller StatController
does not work because it gives the following error
Error: vm.$on is not a function. (In 'vm.$on', 'vm.$on' is undefined)
That is, it vm.$on
is not a function or it is undefined.
Ask
Apparently when I use var vm = this;
the the is not passed $scope
to the variable vm
and therefore functions like $on
are not accessible in my variable vm
.
Is there a way to pass the function $scope.on
to the variable vm
? Could you help me understand how this
vs works $scope
?
Directly answering your question
No. They are different things. The function
$on
is a property of scopes used to emit events, it has nothing to do with the value ofthis
in a controller.I explain.
The reason for your confusion is because you think that
$scope
andthis
have some kind of direct relationship and this is not the case, in reality itthis
is just one more property associated with$scope
your controller (just like$on
and$emit
).Reviewing:
Every time you create a controller, a new
$scope
one is created internally using the method$scopepadre.new()
, this happens because itng-controller
is a directive that is created with the following configurationThe part that says
scope: true
is what is responsible for informing the service$compile
that a new$scope
.How does he get
this
into all this?Your controllers are always created as some kind of class (this is why they should be named in uppercase by convention), like this
If you read how the operator works
new
in javascript and what its impact on the value ofthis
(check step 2) you will realize that when you create an object in this way any property you add tothis
it is being added to the "instance of the class" as such. This is purely a comparison since there are no classes in javascript but I think it illustrates the point pretty well.What does all of the above have to do with
this
angular?Well, when you use the option
controllerAs
in this way'MyController as foo'
, all you do is tell Angular to create a new property called'foo'
in the $scope associated with the element and to assign the newly created controller instance to that property.The latter is important to understand well, scopes are not a feature of controllers, scopes are associated with DOM elements, the most notable example of this is the directive
ng-repeat
that creates a new one$scope
for each element of a collection.We are going to compare the traditional notation with the notation
controllerAs
so you can see what the differences areIn this example, in both controllers, a named property
vm
is created with an empty object and properties are added to it that are bound in the view. In the traditional way you have to do all these steps manually while in the traditional waycontrollerAs
this is not necessary since when objects are instantiated in javascript a new empty object is created (step 1). The property in$scope
it is created in the same declaration as the directive.ng-controller="SampleCtrl as vm"
What is this for?
Well, because angular is javascript it is sometimes necessary to use a
.
(or what is known as "dot notation") to reference some objects since in javascript these references can be lost due to how the prototypical inheritance works in angular (and in javascript generally). This way we make sure that we are referencing the correct object.In the following example you are not using dot notation so when you edit the property in the child controller it works as expected but when you try to do it in its parent it does nothing.
This would be the correct way to do it
In short
controllerAs
, it ensures that we do not fall into traps like that, we just have to understand how it works. He$scope
still has the same responsibilities as always (do$watch
,$emit
,$broadcast
, etc) andthis
he is not intended in any way to replace the functionality of it. It simply guarantees us good practices and sometimes saves us from having to inject$scope
if it is not necessary.Here I leave you some articles (in English unfortunately) in which you can find more details of the subject in question
https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
http://www.egghead.io/video/DTx23w4z6Kc
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#style-y030
you should do something like this:
I also recommend you read something about ES6 since you can generate object classes and methods with angular programming will be easier for you