Angular2 component's "this" is undefined when executing callback function
JavascriptArraysTypescriptAngularJavascript Problem Overview
I have a component which calls a service to fetch data from a RESTful endpoint. This service needs to be given a callback function to execute after fetching said data.
The issue is when I try use the callback function to append the data to the existing data in a component's variable, I get a EXCEPTION: TypeError: Cannot read property 'messages' of undefined
. Why is this
undefined?
TypeScript version: Version 1.8.10
Controller code:
import {Component} from '@angular/core'
import {ApiService} from '...'
@Component({
...
})
export class MainComponent {
private messages: Array<any>;
constructor(private apiService: ApiService){}
getMessages(){
this.apiService.getMessages(gotMessages);
}
gotMessages(messagesFromApi){
messagesFromApi.forEach((m) => {
this.messages.push(m) // EXCEPTION: TypeError: Cannot read property 'messages' of undefined
})
}
}
Javascript Solutions
Solution 1 - Javascript
Use the Function.prototype.bind function:
getMessages() {
this.apiService.getMessages(this.gotMessages.bind(this));
}
What happens here is that you pass the gotMessages
as a callback, when that is being executed the scope is different and so the this
is not what you expected.
The bind
function returns a new function that is bound to the this
you defined.
You can, of course, use an arrow function there as well:
getMessages() {
this.apiService.getMessages(messages => this.gotMessages(messages));
}
I prefer the bind
syntax, but it's up to you.
A third option so to bind the method to begin with:
export class MainComponent {
getMessages = () => {
...
}
}
Or
export class MainComponent {
...
constructor(private apiService: ApiService) {
this.getMessages = this.getMessages.bind(this);
}
getMessages(){
this.apiService.getMessages(gotMessages);
}
}
Solution 2 - Javascript
Or you can do it like this
gotMessages(messagesFromApi){
let that = this // somebody uses self
messagesFromApi.forEach((m) => {
that.messages.push(m) // or self.messages.push(m) - if you used self
})
}
Solution 3 - Javascript
Because you're just passing the function reference in getMessages
you don't have the right this
context.
You can easily fix that by using a lambda which automatically binds the right this
context for the use inside that anonymous function:
getMessages(){
this.apiService.getMessages((data) => this.gotMessages(data));
}
Solution 4 - Javascript
I have same issue, resolved by using () => { } instead function()
Solution 5 - Javascript
Please define function
gotMessages = (messagesFromApi) => {
messagesFromApi.forEach((m) => {
this.messages.push(m)
})
}