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

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

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

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

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


0

Veuillez définir la fonction

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.