Das "this" der Angular2-Komponente ist beim Ausführen der Rückruffunktion undefiniert

92

Ich habe eine Komponente, die einen Dienst aufruft, um Daten von einem RESTful-Endpunkt abzurufen. Dieser Dienst muss eine Rückruffunktion erhalten, die er nach dem Abrufen der Daten ausführen kann.

Das Problem ist, wenn ich versuche, die Rückruffunktion zu verwenden, um die Daten an die vorhandenen Daten in der Variablen einer Komponente anzuhängen, erhalte ich eine EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Warum ist thisundefiniert?

TypeScript-Version: Version 1.8.10

Controller-Code:

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
quelle
Welche Version von TypeScript verwenden Sie? (Sie können das überprüfen mit tsc -v)
Rinukkusu
Die Ausnahme, weil für jeden. Verwenden Sie stattdessen For-of.
Pax Beach

Antworten:

155

Verwenden Sie die Funktion Function.prototype.bind:

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

Was hier passiert, ist, dass Sie das gotMessagesals Rückruf übergeben. Wenn dies ausgeführt wird, ist der Bereich anders und das thisist nicht das, was Sie erwartet haben.
Die bindFunktion gibt eine neue Funktion zurück, die an die von thisIhnen definierte gebunden ist .

Natürlich können Sie dort auch eine Pfeilfunktion verwenden :

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

Ich bevorzuge die bindSyntax, aber es liegt an Ihnen.

Eine dritte Option, um die Methode zunächst zu binden:

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

Oder

export class MainComponent {
    ...

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

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}
Nitzan Tomer
quelle
1
Hat funktioniert, danke! Und danke, dass Sie erklärt haben, warum dies passiert.
Michael Gradek
Danke dir! Die Tatsache, dass im OOP-Stil Javascript-Besonderheiten (bind) verloren gehen, ist tragisch.
Skfd
21

Oder du kannst es so machen

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
quelle
13

Da Sie nur die Funktionsreferenz übergeben, haben getMessagesSie nicht den richtigen thisKontext.

Sie können dies leicht beheben, indem Sie ein Lambda verwenden, das automatisch den richtigen thisKontext für die Verwendung innerhalb dieser anonymen Funktion bindet :

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}
Rinukkusu
quelle
Das war's! Vielen Dank
Michael Gradek
Perfekt. Einfachste und effizienteste Lösung.
Kon
1

Ich habe das gleiche Problem, das mit () => {} statt function () behoben wurde.

Bharathiraja
quelle
0

Bitte definieren Sie die Funktion

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