«This» du composant Angular2 n'est pas défini lors de l'exécution de la fonction de rappel

92

J'ai un composant qui appelle un service pour récupérer des données à partir d'un point de terminaison RESTful. Ce service doit recevoir une fonction de rappel à exécuter après avoir récupéré lesdites données.

Le problème est que lorsque j'essaie d'utiliser la fonction de rappel pour ajouter les données aux données existantes dans la variable d'un composant, j'obtiens un EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Pourquoi est thisindéfini?

Version TypeScript: Version 1.8.10

Code contrôleur:

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
la source
Quelle version de TypeScript utilisez-vous? (Vous pouvez vérifier cela avec tsc -v)
rinukkusu
L'exception car forEach. Utilisez plutôt For-of.
Pax Beach

Réponses:

155

Utilisez la fonction Function.prototype.bind :

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

Ce qui se passe ici, c'est que vous passez le gotMessagescomme un rappel, lorsque cela est exécuté, la portée est différente et donc ce thisn'est pas ce que vous attendiez.
La bindfonction renvoie une nouvelle fonction liée à celle que thisvous avez définie.

Vous pouvez, bien sûr, utiliser une fonction de flèche là aussi:

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

Je préfère la bindsyntaxe, mais c'est à vous de décider.

Une troisième option pour lier la méthode pour commencer:

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

Ou

export class MainComponent {
    ...

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

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}
Nitzan Tomer
la source
1
A travaillé, merci! Et merci d'avoir expliqué pourquoi cela se produit.
Michael Gradek
Je vous remercie! Le fait que le style POO fuit des spécificités javascript (bind) est tragique.
skfd
21

Ou tu peux le faire comme ça

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
la source
13

Parce que vous ne faites que passer la référence de fonction, getMessagesvous n'avez pas le bon thiscontexte.

Vous pouvez facilement résoudre ce problème en utilisant un lambda qui lie automatiquement le bon thiscontexte pour l'utilisation à l'intérieur de cette fonction anonyme:

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}
rinukkusu
la source
C'était ça! Merci
Michael Gradek
Parfait. Solution la plus simple et efficace.
Kon
1

J'ai le même problème, résolu en utilisant () => {} à la place function ()

Bharathiraja
la source
0

Veuillez définir la fonction

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Tài
la source