Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass callback in Angular2/Typescript with reference to this

Passing instance method in Angular2.

When calling login() from the template in the following code, I get this error:

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)

In the following code:

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

I have tried passing this and success and then calling t[success]() in the service, but this produces the exact same error.

My service implements a client side "load balancer" with the circuit breaker pattern, which is the reason why I pass the success/failure function to reuse my code as much as possible.

The service is using rxjs with toPromise, e.g. httpService(...).toPromise().then(success).catch(response => {(circuit breaker pattern on some failures)})

like image 765
YnkDK Avatar asked Jun 11 '26 08:06

YnkDK


2 Answers

You need to bind this, otherwise this within the callback will point to the caller.

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

an alternative way is to use arrow function

login() : void {
    this.authLoginService.login(
        this.username,
        this.password,
        (value) => this.success(value), 
        (value) => this.failure(value)
    )
}
like image 126
Günter Zöchbauer Avatar answered Jun 13 '26 10:06

Günter Zöchbauer


Methods that are supposed to be used as callbacks by design can be bound to the context on their definition. This way there's no chance that they will be accidentally passed without proper context.

This can be achieved with arrow:

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

Or by binding the method in constructor:

constructor(...) {
  this.success = this.success.bind(this);
  ...
}

The second way has an advantage, it allows to spy/mock method as AuthLoginComponent.prototype.success.

like image 28
Estus Flask Avatar answered Jun 13 '26 09:06

Estus Flask



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!