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

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

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

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

¡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 ()


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

esto no agrega ninguna información a las respuestas existentes
DerMike
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.