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

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

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

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

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.


0

Bitte definieren Sie die Funktion

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.