Reputation: 494
I have a controller that is supposed to write text from an input field to the screen. If I use this controller by itself everything works as expected:
(function() {
angular.module('test', []);
function OneCtrl() {
vm = this;
vm.changeHandler = changeHandler;
vm.item = "";
vm.value = "";
}
angular
.module('test')
.controller('OneCtrl', OneCtrl);
var changeHandler = function() {
vm.value = vm.item;
console.log(vm.item);
};
})();
Try here: http://codepen.io/minuskruste/pen/qdrZqq
However, if I add another controller with the same behavior something really weird happens. First of all, the input from field 1 is not sent to console anymore and the text is also not inserted into the html body. Second of all, when I type something into input field 2 it behaves correctly. If I now go back to field 1 and type there, suddenly field 2 input is output to console, even though controller two was never told to do so! This is controller 2:
(function(){
function TwoController(){
vm = this;
vm.changeHandler = changeHandler;
vm.item = "";
vm.value = "";
}
angular
.module('test')
.controller('TwoController', TwoController);
var changeHandler = function() {
vm.value = vm.item;
};
})();
Try here: http://codepen.io/minuskruste/pen/QbpNdY
Is this normal behavior? I was very surprised by it. I also checked if maybe the changeHandler()
leaked to global space but since I've put everything in closures that's not the case. Furthermore this is consistent over different platforms i.e. Chrome and FF. Any ideas?
Upvotes: 0
Views: 57
Reputation: 2300
Part of the issue you are having is that you are declaring vm
without the var
keyword, which makes it a global variable.
However, vm
with the var
keyword is in the local scope of the controller. As a result, it's not available to the changeHandler()
anymore. If you reorder your code and declare changeHandler()
inside the controller, it will work.
(function() {
angular.module('test', []);
function OneCtrl() {
var vm = this;
vm.item = "";
vm.value = "";
vm.changeHandler = function() {
vm.value = vm.item;
console.log(vm.item);
}
}
angular
.module('test')
.controller('OneCtrl', OneCtrl);
})();
(function(){
function TwoController() {
var vm = this;
vm.item = "";
vm.value = "";
vm.changeHandler = function() {
vm.value = vm.item;
console.log(vm.item);
}
}
angular
.module('test')
.controller('TwoController', TwoController);
})();
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<body ng-app="test">
<h1>Choice array</h1>
<div>
<form novalidate ng-controller="OneCtrl as one">
<input type="text" ng-change="one.changeHandler()" ng-model="one.item">
<div>{{one.value}}</div>
</form>
<br><br><br>
</div>
<div>
<form novalidate ng-controller="TwoController as two">
<input type="text" ng-change="two.changeHandler()" ng-model="two.item">
<div>{{two.value}}</div>
</form>
</div>
</body>
Upvotes: 1
Reputation: 563
Here is a good article on the Controller As syntax.
http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
If you declare the vm variable in each controller it will prevent the overwriting behavior you are seeing. Javascript is functional scoped, which means if it doesn't find the variable declaration for vm in the current function, it will go up the prototypical chain until it finds the declaration (var vm). If it doesn't find any declaration in the global scope, it will automatically create one for you. By declaring inside each controller you will prevent them from both sharing the same global scope.
function OneCtrl() {
// This construction makes sure I know which context is addressed. I can now hand vm (view-model) inside an object and the context doesn't change.
var vm = this;
vm.item = "";
vm.value = "";
vm.changeHandler = function() {
console.log(vm.item);
vm.value = vm.item;
};
}
http://plnkr.co/edit/KdZvG7d2COLNcjRIfyHb?p=preview
Upvotes: 0
Reputation: 4050
This happens because you're using global "vm" variable, which containg this reference of controller. And with second controller you're overwriting vm variable with reference to second controller.
I've updated your code to use proper this references
Also angular supports another data binding approach with special $scope object: https://docs.angularjs.org/guide/scope
(function() {
angular.module('test', []);
function OneCtrl($scope) {
// This construction makes sure I know which context is addressed. I can now hand vm (view-model) inside an object and the context doesn't change.
this.changeHandler = angular.bind(this, changeHandler);
this.item = "";
this.value = "";
}
angular
.module('test')
.controller('OneCtrl', OneCtrl);
var changeHandler = function() {
this.value = this.item;
console.log(this.item);
};
})();
(function(){
function TwoController(){
// This construction makes sure I know which context is addressed. I can now hand vm (view-model) inside an object and the context doesn't change.
this.changeHandler = angular.bind(this, changeHandler);
this.item = "";
this.value = "";
}
angular
.module('test')
.controller('TwoController', TwoController);
var changeHandler = function() {
this.value = this.item;
};
})();
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<body ng-app="test">
<h1>Choice array</h1>
<div>
<form novalidate ng-controller="OneCtrl as one">
<input
type="text"
ng-change="one.changeHandler()" ng-model="one.item">
<div>{{one.value}}</div>
</form>
<br>
<br>
<br>
</div>
<div>
<form novalidate ng-controller="TwoController as two">
<input
type="text"
ng-change="two.changeHandler()" ng-model="two.item">
<div>{{two.value}}</div>
</form>
</div>
</body>
Upvotes: 1