Передайте обратный вызов в Angular2 / Typescript со ссылкой на это


Передача метода экземпляра в Angular2.

При вызове login() из шаблона в следующем коде, я получаю эту ошибку:

Failure TypeError: Cannot read property 'router' of null
    at AuthLoginComponent.success (auth-login.component.ts:30)
    at ZoneDelegate.invoke (zone.js:242)
    at Object.onInvoke (core.umd.js:4391)
    at ZoneDelegate.invoke (zone.js:241)
    at Zone.run (zone.js:113)
    at zone.js:520
    at ZoneDelegate.invokeTask (zone.js:275)
    at Object.onInvokeTask (core.umd.js:4382)
    at ZoneDelegate.invokeTask (zone.js:274)
    at Zone.runTask (zone.js:151)
    at drainMicroTaskQueue (zone.js:418)
    at XMLHttpRequest.ZoneTask.invoke (zone.js:349)

В следующем коде:

@Component({
    moduleId: module.id,
    selector: "app-auth-login",
    templateUrl: "/app/components/auth/login/auth-login.component.html"
})
export class AuthLoginComponent implements OnInit {
    username : "";
    password : "";

    constructor(
        private authLoginService: AuthLoginService,
        private router: Router
    ) {}

    ngOnInit(): void {
    }

    success(value: Response) : void {
        console.log("Success", value);
        this.router.navigate(["/home"]);
    }

    failure(value: Response) : void {
        console.log("Failure", value);
    }

    login() : void {
        this.authLoginService.login(
            this.username,
            this.password,
            this.success,
            this.failure
        )
    }
}

Я пробовал передавать this и success, а затем вызывать t[success]() в службе, но это приводит к точно такой же ошибке.

В моем сервисе реализован клиентский "балансировщик нагрузки" с шаблоном автоматического выключателя, поэтому я передаю функцию success/failure для повторного использования моего кода как можно чаще. по возможности.

Служба использует rxjs с toPromise, например httpService(...).toPromise().then(success).catch(response => {(circuit breaker pattern on some failures)})

2 2

2 ответа:

Нужно привязать this, иначе this внутри обратного вызова будет указывать на вызывающего абонента.

login() : void {
    this.authLoginService.login(
        this.username,
        this.password,
        this.success.bind(this),
        this.failure.bind(this)
    )
}

Альтернативным способом является использование функции стрелки

login() : void {
    this.authLoginService.login(
        this.username,
        this.password,
        (value) => this.success(value), 
        (value) => this.failure(value)
    )
}

Методы, которые предполагается использовать в качестве обратных вызовов при проектировании, могут быть привязаны к контексту при их определении. Таким образом, нет никакого шанса, что они будут случайно переданы без надлежащего контекста.

Это может быть достигнуто с помощью стрелки:

success = (value: Response) : void => { ... }

Или путем привязки метода в конструкторе:

constructor(...) {
  this.success = this.success.bind(this);
  ...
}
Второй способ имеет преимущество, он позволяет шпионить / имитировать метод как AuthLoginComponent.prototype.success.