"This" dari komponen Angular2 tidak ditentukan saat menjalankan fungsi callback

94

Saya memiliki komponen yang memanggil layanan untuk mengambil data dari titik akhir RESTful. Layanan ini perlu diberi fungsi panggilan balik untuk dijalankan setelah mengambil data tersebut.

Masalahnya adalah ketika saya mencoba menggunakan fungsi panggilan balik untuk menambahkan data ke data yang ada di variabel komponen, saya mendapatkan file EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Mengapa tidak thisditentukan?

Versi TypeScript: Versi 1.8.10

Kode pengontrol:

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
sumber
Versi TypeScript mana yang Anda gunakan? (Anda dapat memeriksanya dengan tsc -v)
rinukkusu
Pengecualian karena forEach. Gunakan For-of sebagai gantinya.
Pax Beach

Jawaban:

159

Gunakan fungsi Function.prototype.bind :

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

Apa yang terjadi di sini adalah Anda meneruskan gotMessagessebagai callback, ketika itu sedang dijalankan cakupannya berbeda sehingga thistidak seperti yang Anda harapkan.
Itubind mengembalikan fungsi fungsi baru yang terikat pada thisAnda tetapkan.

Anda tentu saja dapat menggunakan fungsi panah di sana:

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

Saya lebih suka bind sintaks, tetapi terserah Anda.

Opsi ketiga untuk mengikat metode untuk memulai:

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

Atau

export class MainComponent {
    ...

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

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}
Nitzan Tomer
sumber
1
Berhasil, terima kasih! Dan terima kasih telah menjelaskan mengapa ini terjadi.
Michael Gradek
Terima kasih! Fakta bahwa OOP-style membocorkan spesifikasi javascript (bind) itu tragis.
skfd
21

Atau Anda bisa melakukannya seperti ini

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

Karena Anda hanya meneruskan referensi fungsi, getMessagesAnda tidak memiliki thiskonteks yang tepat .

Anda dapat dengan mudah memperbaikinya dengan menggunakan lambda yang secara otomatis mengikat thiskonteks yang tepat untuk penggunaan di dalam fungsi anonim itu:

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}
rinukkusu
sumber
Itu dia! Terima kasih
Michael Gradek
Sempurna. Solusi paling sederhana dan efisien.
Kon
1

Saya memiliki masalah yang sama, diselesaikan dengan menggunakan () => {} sebagai gantinya function ()

Bharathiraja
sumber
ini tidak menambahkan informasi apapun ke jawaban yang ada
DerMike
0

Harap tentukan fungsi

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Tài
sumber
ini tidak menambahkan informasi apapun ke jawaban yang ada
DerMike