"This" компонента Angular2 не определено при выполнении функции обратного вызова.

94

У меня есть компонент, который вызывает службу для получения данных из конечной точки RESTful. Этой службе необходимо предоставить функцию обратного вызова, которая будет выполняться после получения указанных данных.

Проблема в том, что когда я пытаюсь использовать функцию обратного вызова для добавления данных к существующим данным в переменной компонента, я получаю файл EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Почему не thisопределено?

Версия TypeScript: Версия 1.8.10

Код контроллера:

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
        })
    }
}
Майкл Градек
источник
Какую версию TypeScript вы используете? (Вы можете проверить это с помощью tsc -v)
rinukkusu
Исключение, потому что forEach. Вместо этого используйте For-of.
Pax Beach

Ответы:

159

Используйте функцию Function.prototype.bind :

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

Здесь происходит то, что вы передаете gotMessagesкак обратный вызов, когда он выполняется, область видимости отличается, и поэтому thisэто не то, что вы ожидали. Функция возвращает новую функцию , которая привязана к вы определили.
bindthis

Конечно, вы можете использовать и стрелочную функцию :

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

Я предпочитаю bindсинтаксис, но решать вам.

Третий вариант, чтобы привязать метод для начала:

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

Или

export class MainComponent {
    ...

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

    getMessages(){
        this.apiService.getMessages(gotMessages);
    }
}
Ницан Томер
источник
1
Сработало, спасибо! И спасибо, что объяснили, почему это происходит.
Michael Gradek 07
Спасибо! Тот факт, что в ООП-стиле теряется специфика javascript (привязка), трагичен.
skfd
21

Или вы можете сделать это так

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

Поскольку вы просто передаете ссылку на функцию, у getMessagesвас нет правильного thisконтекста.

Вы можете легко исправить это, используя лямбду, которая автоматически связывает правильный thisконтекст для использования внутри этой анонимной функции:

getMessages(){
    this.apiService.getMessages((data) => this.gotMessages(data));
}
ринуккусу
источник
Это было! Спасибо
Майкл Градек 07
Отлично. Самое простое и эффективное решение.
Кон,
1

У меня такая же проблема, которую можно решить, используя () => {} вместо function ()

Бхаратхираджа
источник
это не добавляет никакой информации к существующим ответам
DerMike
0

Пожалуйста, определите функцию

gotMessages = (messagesFromApi) => {
  messagesFromApi.forEach((m) => {
    this.messages.push(m)
  })
}
Тай
источник
это не добавляет никакой информации к существующим ответам
DerMike