2015-01-09 2 views
3

Учитывая этот кусок кода:Почему «this» ссылается на «окно» в этом фрагменте кода?

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); 

Все работает просто отлично, но в методе deleteMovieFromDownloadList в линии this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie);, this ссылается на объект окна, вместо реального объекта я ожидаю.

Сформированный JavaScript выглядит следующим образом:

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 

Как вы можете видеть, в сгенерированном JS, конкретный метод использует _this. Это выглядит правильно, не так ли?

Может ли кто-нибудь объяснить мне, что происходит и как это исправить?

EDIT:

Я использую это в сочетании с угловыми:

<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: После попытки все предложения и затем установить обратно оригинальный кусок кода, который я разместил здесь, все просто отлично работает! Я не знаю, как, но я думаю, что это имеет какое-то отношение к Chrome/VS 2013. В любом случае, благодаря тем, кто пытался мне помочь.

+0

Возможно, функция «deleteMovie ...» привязана к кнопке или другому элементу пользовательского интерфейса. В этом случае эта функция выполняется в контексте окна. Чтобы устранить проблему, вы должны определить тело функции в конструкторе вашего контроллера: this.deleteMovieFromDownloadList = (movie: any) => { this.MovieService.deleteMovieFromDownloadList (movie) .then (() => { debugger; это. $ Scope.moviesToDownload = _.without (это. $ Scope.moviesToDownload, movie); }); } – TSV

+0

Я всегда перемещаю такие функции в конструкторе. Я уверен, что это самый простой способ исправить проблему с помощью «этого» в контроллерах TypeScript. – TSV

ответ

2

Возможно, функция "deleteMovie ..." привязана к кнопке или другому элементу пользовательского интерфейса. В этом случае эта функция выполняется в контексте окна. Чтобы устранить эту проблему, необходимо определить тело функции в конструкторе контроллера:

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); 
    }); 
    } 
} 

и объявить соответствующую функцию в классе контроллера:

deleteMovieFromDownloadList: (movie: any) => void; 
+0

вы можете объявить функцию с внешним контроллером ambda: 'deleteMovieFromDownloadList = (movie: any) => {...}' –

2

Я предполагаю, что проблема заключается в том, как вызывается deleteMovieFromDownloadList. Если вы делаете что-то вроде этого:

var myController = new HomeController(); 
someFramework.doSomethingWithCallback(myController.deleteMovieFromDownloadList); 

... someFramework бы в конечном итоге вызывая функцию обратного вызова без его привязки к ожидаемому контексту. Если это так, то вы можете это исправить, делая это:

var boundCallback = myController.deleteMovieFromDownloadList.bind(myController); 
someFramework.doSomethingWithCallback(boundCallback); 

Update:

При чтении через угловые документы, оказывается, что то, что вы положили в обработчик щелчка не очень JavaScript, но интерпретируется Angular. Так что, может быть, homeCtrl.deleteMovieFromDownloadList(m) делает что-то вроде этого, по существу:

var fn = homeCtrl.deleteMovieFromDownloadList; 
fn(m): 

... который, конечно, не будет установлен this правильно.

С установленным контроллером вы не можете это сделать?

data-ng-click="deleteMovieFromDownloadList(m)" 
+0

Спасибо, но нет, я не могу этого сделать, потому что этот метод не находится в '$ scope'. Вот почему я использую синтаксис 'ControllerAs'. – Martijn

1

Javascript имеет некоторые странное поведение, когда речь идет о конструкторские функции.Я привел пример, чтобы показать вам, как это работает:

function myClass() { 
    var me = this; 
    this.property = 'test'; 
    this.windowObj = function() { 
     windowObjTest(); 
    } 

    var windowObjTest = function() { 
     console.log(this); // this = window object 
    } 

    this.myself = function() { 
     myselfTest(); 
    } 

    var myselfTest = function() { 
     console.log(me); // me = this object 
    } 
} 

var myobj = new myClass(); 
myobj.windowObj(); 
myobj.myself(); 

this.windowObj(); является государственной функцией, чтобы вызвать закрытую функцию windowObjTest();. Внутри частной функции this является ссылкой на объект окна вместо этого экземпляра (не спрашивайте меня, dev был высоким или что-то еще ...). Чтобы получить ссылку на текущий объект на частные функции, вы должны установить ссылку на свой текущий объект в свою конструкторскую функцию: var me = this;.

Попробуйте пример и загляните в консоль. Должны появиться две записи:

- Window test.php //console.log(this); 
- myClass { property="test", windowObj=function(), myself=function()} //console.log(me); 
+0

Спасибо, я знаком с этим. Как вы можете видеть в сгенерированном JS-коде, он уже делает то, что вы подразумеваете: 'var _this = this;', и именно по этой причине я не понимаю, почему он не работает, потому что в обратном вызове этой функции он использует '_this'. – Martijn

+0

Я не уверен, что _this = это правильно в этом контексте. Вы должны установить _this = this в конструкторе класса, чтобы использовать его вместо частной функции 'HomeController.prototype.deleteMovieFromDownloadList'. В этом месте вы перезаписываете _this с помощью объекта window. – Tyr

+0

Я думаю, что это правильно, потому что в отладчике я вижу, что когда вызывается '_this = this',' this' относится к правильному объекту. – Martijn

Смежные вопросы