2

Please check the code below. I've edited a ionic example of popup just to illustrate.

On showAlert1(), I reference 'this' as is and it doesn't work. On showAlert2(), I use a auxiliar variable '_this' that receives 'this' and it does works.

I've seen this kind of thing happening in other occasions, and I believe it's 'Controller as' syntax scope related, but why does this happens?

angular.module('myApp', ['ionic'])
.controller('PopupCtrl',function($scope, $ionicPopup) {

   this.testAlert = function() {
     alert('Alerting!');
   };

   this.showAlert1 = function() {
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, this).then(function() {
       this.testAlert();
     });
   };

   this.showAlert2 = function() {
     var _this = this;
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, _this).then(function() {
       _this.testAlert();
     });
   };

});

Here's a Code Pen: http://codepen.io/anon/pen/dPJVNN

Thanks!

Som
  • 307
  • 2
  • 10
  • possible duplicate of [How does the "this" keyword work?](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – JLRishe Feb 11 '15 at 05:21

3 Answers3

4

"this" in javascript is not the same as "this" in other languages. You can think of it more as the context of a function call.

The default call context on a web application is window. However, when calling a function that is a property of an object, the context becomes the object.

So, in your example:

angular.module('myApp', ['ionic'])
.controller('PopupCtrl',function($scope, $ionicPopup) {
   //"this" refers to the controller instance here (controllers are created by angular with the "new" operator)
   this.testAlert = function() {
     //inside of this function, "this" will still be the controller
     alert('Alerting!');
   };

   //"this" is the controller
   this.showAlert1 = function() {
   //"this" is still the controller
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, this).then(function() {
       //"this" is no longer the controller.  It's probably "window", but it's possible that ionic sets this to some other parameter when it invokes the function.
       //since it's not the controller, testAlert() is undefined!
       this.testAlert();
     });
   };

   //"this" is the controller
   this.showAlert2 = function() {
     //"this" is still the controller, and you have assigned _this to also be the controller
     var _this = this;
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, _this).then(function() {
       //"_this" is captured by the closure created by the function call, and it is still the controller, so testAlert() is defined. 
       _this.testAlert();
     });
   };

});

You'll often see this in code:

var self = this;

With "self" being used in place of this in order to avoid the confusion that you've encountered.

angular.module('myApp', ['ionic'])
.controller('PopupCtrl',function($scope, $ionicPopup) {
   var self = this;
   self.testAlert = function() {
     alert('Alerting!');
   };

   self.showAlert1 = function() {
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, self).then(function() {
       self.testAlert();
     });
   };

   self.showAlert2 = function() {

     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, self).then(function() {
       self.testAlert();
     });
   };

});
Joe Enzminger
  • 10,742
  • 2
  • 45
  • 74
  • Great explanation. Thanks! Is there a way to know the context of all the object, something like a context tree? – Som Feb 11 '15 at 18:02
0

The keyword 'this' refers to the current object it's in. So in your showAlert1() function, the 'this' refers to the $ionicPopup object.

So for example

var parent = {
    value: 'parent',
    alert: function(){
        alert(this.value);
    },
    child: {
        alert: function(){
            alert(this.value);
        }
    }
}

if you do a parent.alert(), it will alert parent. But if you do a parent.child.alert() it will give you undefined because the 'this.value' for child doesnt exist, it doesnt refer to parent.value. So what this means is that this refers to the current object.

yangli1990
  • 14,642
  • 6
  • 29
  • 37
0

"this" in the .then() callback in showAlert1 is referring to the global object. Try this out:

angular.module('mySuperApp', ['ionic'])
.controller('PopupCtrl',function($scope, $ionicPopup) {

   this.testAlert = function() {
     alert('Alerting!');
   };

   this.showAlert1 = function() {
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, this).then(function() {
       alert(this === window);
       this.testAlert();
     });
   };

   this.showAlert2 = function() {
     var _this = this;
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }, _this).then(function() {
       _this.testAlert();
     });
   };

});

"this" is only working for showAlert1 and showAlert2 because you're referring to them as a property of the controller itself right here:

<button class="button button-primary" ng-click="popup.testAlert()">

What is preferable is using $scope:

<html ng-app="mySuperApp">
  <head>
    <meta charset="utf-8">
    <title>
      Popups
    </title>

    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">

    <link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
    <script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>

  </head>
  <body class="padding" ng-controller="PopupCtrl">
    <button class="button button-primary" ng-click="testAlert()">
      Test Alert
    </button>
    <button class="button button-positive" ng-click="showAlert1()">
      Alert1
    </button>
    <button class="button button-positive" ng-click="showAlert2()">
      Alert2
    </button>

    <script id="popup-template.html" type="text/ng-template">
      <input ng-model="data.wifi" type="text" placeholder="Password">
    </script>
  </body>
</html>

then the JS:

angular.module('mySuperApp', ['ionic'])
.controller('PopupCtrl',function($scope, $ionicPopup) {

   $scope.testAlert = function() {
     alert('Alerting!');
   };

   $scope.showAlert1 = function() {
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }).then(function() {
       $scope.testAlert();
     });
   };

   $scope.showAlert2 = function() {
     $ionicPopup.alert({
       title: 'Don\'t eat that!',
       template: 'It might taste good'
     }).then(function() {
       $scope.testAlert();
     });
   };
});
catfood
  • 389
  • 4
  • 5