El "esto" del componente Angular2 no está definido al ejecutar la función de devolución de llamada

94

Tengo un componente que llama a un servicio para obtener datos de un punto final RESTful. Este servicio debe recibir una función de devolución de llamada para que se ejecute después de obtener dichos datos.

El problema es que cuando intento usar la función de devolución de llamada para agregar los datos a los datos existentes en la variable de un componente, obtengo un EXCEPTION: TypeError: Cannot read property 'messages' of undefined. ¿Por qué thisno está definido?

Versión de TypeScript: Versión 1.8.10

Código del 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
        })
    }
}
Michael Gradek
fuente
¿Qué versión de TypeScript estás usando? (Puedes comprobarlo con tsc -v)
rinukkusu
La excepción porque forEach. Utilice For-of en su lugar.
Pax Beach

Respuestas:

159

Utilice la función Function.prototype.bind :

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

Lo que sucede aquí es que pasa gotMessagescomo una devolución de llamada, cuando se está ejecutando, el alcance es diferente y, por lo tanto, thisno es lo que esperaba.
La bindfunción devuelve una nueva función que está vinculada al thisdefinido por usted.

Por supuesto, también puede usar una función de flecha allí:

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

Prefiero la bindsintaxis, pero tú decides.

Una tercera opción para vincular el método para comenzar:

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

O

export class MainComponent {
    ...

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

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}
Nitzan Tomer
fuente
1
Funcionó, gracias! Y gracias por explicar por qué sucede esto.
Michael Gradek
¡Gracias! El hecho de que el estilo OOP esté filtrando datos específicos de JavaScript (enlace) es trágico.
skfd
21

O puedes hacerlo así

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

Debido a que solo está pasando la referencia de la función getMessages, no tiene el thiscontexto correcto .

Puede solucionarlo fácilmente utilizando una lambda que enlaza automáticamente el thiscontexto correcto para el uso dentro de esa función anónima:

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}
rinukkusu
fuente
¡Eso fue todo! Gracias
Michael Gradek
Perfecto. La solución más sencilla y eficaz.
Kon
1

Tengo el mismo problema, resuelto usando () => {} en lugar de function ()

Bharathiraja
fuente
esto no agrega ninguna información a las respuestas existentes
DerMike
0

Defina la función

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Tài
fuente
esto no agrega ninguna información a las respuestas existentes
DerMike