„This” komponentu Angular2 jest niezdefiniowane podczas wykonywania funkcji zwrotnej

94

Mam komponent, który wywołuje usługę do pobierania danych z punktu końcowego RESTful. Ta usługa musi otrzymać funkcję zwrotną do wykonania po pobraniu wspomnianych danych.

Problem polega na tym, że gdy próbuję użyć funkcji wywołania zwrotnego, aby dołączyć dane do istniejących danych w zmiennej składnika, otrzymuję plik EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Dlaczego jest thisnieokreślony?

Wersja TypeScript: Wersja 1.8.10

Kod kontrolera:

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
źródło
Której wersji języka TypeScript używasz? (Możesz to sprawdzić tsc -v)
rinukkusu
Wyjątek, ponieważ forEach. Zamiast tego użyj For-of.
Pax Beach

Odpowiedzi:

159

Użyj funkcji Function.prototype.bind :

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

To, co się tutaj dzieje, to to, że przekazujesz gotMessageswywołanie zwrotne jako wywołanie zwrotne, gdy jest ono wykonywane, zakres jest inny, więc thisnie jest to, czego oczekiwałeś. Funkcja zwraca nową funkcję, która jest związana z zdefiniowano.
bindthis

Możesz oczywiście użyć tam również funkcji strzałek :

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

Wolę bindskładnię, ale to zależy od Ciebie.

Trzecia opcja, aby powiązać metodę na początek:

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

Lub

export class MainComponent {
    ...

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

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}
Nitzan Tomer
źródło
1
Pracowałem, dzięki! Dziękuję za wyjaśnienie, dlaczego tak się dzieje.
Michael Gradek
Dziękuję Ci! Fakt, że styl OOP przecieka specyfikacje javascript (bind) jest tragiczny.
skfd
21

Lub możesz to zrobić w ten sposób

gotMessages(messagesFromApi){
    let that = this // somebody uses self 
    messagesFromApi.forEach((m) => {
        that.messages.push(m) // or self.messages.push(m) - if you used self
    })
}
Michał Kliment
źródło
13

Ponieważ po prostu przekazujesz odwołanie do funkcji getMessages, nie masz odpowiedniego thiskontekstu.

Możesz to łatwo naprawić, używając lambdy, która automatycznie wiąże odpowiedni thiskontekst do użycia w tej anonimowej funkcji:

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}
rinukkusu
źródło
To było to! Dzięki
Michael Gradek
Idealny. Najprostsze i wydajne rozwiązanie.
Kon
1

Mam ten sam problem, rozwiązany za pomocą () => {} zamiast funkcji ()

Bharathiraja
źródło
to nie dodaje żadnych informacji do istniejących odpowiedzi
DerMike
0

Określ funkcję

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Tài
źródło
to nie dodaje żadnych informacji do istniejących odpowiedzi
DerMike