Angular2 component's "this" is undefined when executing callback function

JavascriptArraysTypescriptAngular

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichael GradekView Question on Stackoverflow
Solution 1 - JavascriptNitzan TomerView Answer on Stackoverflow
Solution 2 - JavascriptMichal KlimentView Answer on Stackoverflow
Solution 3 - JavascriptrinukkusuView Answer on Stackoverflow
Solution 4 - JavascriptBharathirajaView Answer on Stackoverflow
Solution 5 - JavascriptTàiView Answer on Stackoverflow