O "this" do componente Angular2 é indefinido ao executar a função de retorno de chamada


92

Eu tenho um componente que chama um serviço para buscar dados de um ponto de extremidade RESTful. Este serviço precisa receber uma função de retorno de chamada para ser executado após a busca dos dados.

O problema é que, quando tento usar a função de retorno de chamada para acrescentar os dados aos dados existentes na variável de um componente, obtenho um EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Por que está thisindefinido?

Versão TypeScript: Versão 1.8.10

Código do controlador:

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

Qual versão do TypeScript você está usando? (Você pode verificar isso com tsc -v)
rinukkusu

A exceção porque forEach. Em vez disso, use For-of.
Pax Beach,

Respostas:


155

Use a função Function.prototype.bind :

getMessages() {
    this.apiService.getMessages(this.gotMessages.bind(this));
}

O que acontece aqui é que você passa o gotMessagescomo callback, quando está sendo executado o escopo é diferente e então thisnão é o que você esperava.
A bindfunção retorna uma nova função que está vinculada ao que thisvocê definiu.

Você pode, é claro, usar uma função de seta lá também:

getMessages() {
    this.apiService.getMessages(messages => this.gotMessages(messages));
}

Eu prefiro a bindsintaxe, mas é com você.

Uma terceira opção para ligar o método para começar:

export class MainComponent {
    getMessages = () => {
        ...
    }
}

Ou

export class MainComponent {
    ...

    constructor(private apiService: ApiService) {
        this.getMessages = this.getMessages.bind(this);
    }

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}

1
Funcionou, obrigado! E obrigado por explicar por que isso acontece.
Michael Gradek

Obrigado! O fato de que o estilo OOP está vazando especificações de javascript (bind) é trágico.
skfd

21

Ou você pode fazer assim

gotMessages(messagesFromApi){
    let that = this // somebody uses self 
    messagesFromApi.forEach((m) => {
        that.messages.push(m) // or self.messages.push(m) - if you used self
    })
}

13

Porque você está apenas passando a referência de função, getMessagesvocê não tem o thiscontexto certo .

Você pode corrigir isso facilmente usando um lambda que vincula automaticamente o thiscontexto certo para o uso dentro dessa função anônima:

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}

Era isso! Obrigado
Michael Gradek

Perfeito. Solução mais simples e eficiente.
Kon

1

Eu tenho o mesmo problema, resolvido usando () => {} em vez da função ()


0

Defina a função

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.