Martijn Martijn - 2 months ago 10
TypeScript Question

Why is 'this' refering to 'window' in this TypeScript snippet?

Given this piece code:

module movieApp {
export interface IHomeControllerScope extends ng.IScope {
moviesToDownload: string[];
active: string;

deleteMovieFromDownloadList(movie: any);

markMovieAsDownloaded(movie: any);
}

export class HomeController {
public static $inject = [
'$scope',
'$location',
'MovieService'
];

constructor(private $scope: IHomeControllerScope, private $location: ng.ILocationService, private MovieService) {
this.$scope.$on('$locationChangeSuccess', (event) => {
this.setActiveUrlPart();
});

MovieService.getMoviesToDownload().then(response => {
this.$scope.moviesToDownload = response;
});
}

private setActiveUrlPart() {
var parts = this.$location.path().split('/');
this.$scope.active = parts[1];
}

public get moviesToDownload() {
return this.$scope.moviesToDownload;
}

public markMovieAsDownloaded(movie: any) {
movie.Downloaded = true;
}

public deleteMovieFromDownloadList(movie: any) {
this.MovieService.deleteMovieFromDownloadList(movie).then(() => {
debugger;
this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie);
});
}
}
}

app.controller("HomeController", movieApp.HomeController);


Everything works just fine, but in the method
deleteMovieFromDownloadList
in the line
this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie);
,
this
refers to the window object, instead of the actual object I expect.

The generated JavaScript looks like this:

var movieApp;
(function (movieApp) {
var HomeController = (function () {
function HomeController($scope, $location, MovieService) {
var _this = this;
this.$scope = $scope;
this.$location = $location;
this.MovieService = MovieService;
this.$scope.$on('$locationChangeSuccess', function (event) {
_this.setActiveUrlPart();
});

MovieService.getMoviesToDownload().then(function (response) {
_this.$scope.moviesToDownload = response;
});
}
HomeController.prototype.setActiveUrlPart = function () {
var parts = this.$location.path().split('/');
this.$scope.active = parts[1];
};

Object.defineProperty(HomeController.prototype, "moviesToDownload", {
get: function () {
return this.$scope.moviesToDownload;
},
enumerable: true,
configurable: true
});

HomeController.prototype.markMovieAsDownloaded = function (movie) {
movie.Downloaded = true;
};

HomeController.prototype.deleteMovieFromDownloadList = function (movie) {
var _this = this;
this.MovieService.deleteMovieFromDownloadList(movie).then(function () {
debugger;
_this.$scope.moviesToDownload = _.without(_this.$scope.moviesToDownload, movie);
});
};
HomeController.$inject = [
'$scope',
'$location',
'MovieService'
];
return HomeController;
})();
movieApp.HomeController = HomeController;
})(movieApp || (movieApp = {}));

app.controller("HomeController", movieApp.HomeController);
//# sourceMappingURL=HomeController.js.map


As you can see, in the generated JS, the specific method uses _this. This looks right, right?

Could someone explain to me what happens and how to fix this?

EDIT:

I use this in combination with Angular:

<body data-ng-app="movieApp" data-ng-controller="HomeController as homeCtrl">
<div class="col-sm-1">
<i class="fa fa-trash-o" data-ng-click="homeCtrl.deleteMovieFromDownloadList(m)" title="Verwijder uit lijst"></i>
</div>
</body>


EDIT II:
After trying all the suggestions and then setting back the original piece of code I've posted here, everything just works fine! I don't know how, but I guess it has something to do with Chrome / VS 2013. Anyway, thanks to those who have tried to help me out.

TSV TSV
Answer

Probably the "deleteMovie..." function is bound to a button or other UI element. In this case this function is executed in the window context. To fix the issue, you should define the function body in the constructor of your controller:

constructor(private $scope: IHomeControllerScope, private $location: ng.ILocationService, private MovieService) {
// other initialization code...

this.deleteMovieFromDownloadList = (movie: any) => {
    this.MovieService.deleteMovieFromDownloadList(movie).then(() => {
        debugger;
        this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie);
    });
  }
}

and declare appropriate function in your controller class:

deleteMovieFromDownloadList: (movie: any) => void;
Comments