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

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

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

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

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


to nie dodaje żadnych informacji do istniejących odpowiedzi
DerMike

0

Określ funkcję

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}

to nie dodaje żadnych informacji do istniejących odpowiedzi
DerMike
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.