I cannot understand how 'this' context works in typescript. I cannot access class members in methods. Below is my code
class adopterDetailCtrl {
public adopter: IAdopter;
public $router: any;
static $inject = ['app.common.services.AdopterService'];
constructor(private adopterService: app.common.services.IAdopterServices) {
this.adopter = null;
}
$routerOnActivate(next) {
if (next.params.id > 0) {
this.getAdopterById(next.params.id);
}
}
getAdopterById(adopterId: number): void {
var AdopterList = this.adopterService.getAdopterById();
AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
});
}
setAdopter(data: IAdopter) {
this.adopter = data;//can access this.adopter
}
}
The this
context is just the same in typescript as it as in javascript, as the code you actually run is the compiled javascript that the typescript compiler outputs.
In javascript you have two ways to deal with this issue:
- Use the arrow function
- Use the Function.prototype.bind function
You are probably passing getAdopterById
as a callback, if that's the case then it will be easy to solve using bind
:
let myobj = new adopterDetailCtrl(...);
...
someFunction(myobj.getAdopterById.bind(myobj));
You can also modify the reference for the method of the instance in the ctor:
(1)
class adopterDetailCtrl {
public adopter: IAdopter;
public $router: any;
static $inject = ['app.common.services.AdopterService'];
constructor(private adopterService: app.common.services.IAdopterServices) {
this.adopter = null;
this.getAdopterById = (adopterId: number) => {
var AdopterList = this.adopterService.getAdopterById();
AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
});
}
}
$routerOnActivate(next) {
if (next.params.id > 0) {
this.getAdopterById(next.params.id);
}
}
getAdopterById: (adopterId: number) => void;
setAdopter(data: IAdopter) {
this.adopter = data;//can access this.adopter
}
}
Notice that the method declaration is empty and the implementation is set in the ctor using the arrow function.
(2)
class adopterDetailCtrl {
public adopter: IAdopter;
public $router: any;
static $inject = ['app.common.services.AdopterService'];
constructor(private adopterService: app.common.services.IAdopterServices) {
this.adopter = null;
this.getAdopterById = this.getAdopterById.bind(this);
}
$routerOnActivate(next) {
if (next.params.id > 0) {
this.getAdopterById(next.params.id);
}
}
getAdopterById(adopterId: number): void {
var AdopterList = this.adopterService.getAdopterById();
AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
});
}
setAdopter(data: IAdopter) {
this.adopter = data;//can access this.adopter
}
}
Here in the ctor you reassign the bound this.getAdopterById.bind(this)
to this.getAdopterById
.
In both of these cases you can freely pass the getAdopterById
method as a callback and not worrying about the scope of this
.
Another note on the arrow functions is that this is a new feature in ES6
, and if you don't choose the ES6
target in your compilation options then the compiler won't actually use this notation but instead will convert this:
class A {
private x: number;
fn(): void {
setTimeout(() => console.log(this.x), 1);
}
}
To:
var A = (function () {
function A() {
}
A.prototype.fn = function () {
var _this = this;
setTimeout(function () { return console.log(_this.x); }, 1);
};
return A;
}());
This way the scope of this
is saved in _this
and in the callback function _this.x
is used instead of this.x
.