Antonio Santoro
Antonio Santoro

Reputation: 907

Passing a function to catchError

I defined a simple function that will open a MatSnackBar and display the error message when a get request returns any error

handleError(err: HttpErrorResponse) {
    this.snackBar.open(err.message)
    return throwError(err)
}

So when I subscribe to an observable in the constructor I pass the above function to catchError

constructor(private snackBack: MatSnackBar) {
...
this._obv$ = this.myService.getTypes().pipe(
  catchError(this.handleError)
)
...

When the error is triggered the err passed to the handleError is correct but this.snackBar is undefined.

The problem disappear when I use the handleError function like this

this._obv$ = this.myService.getTypes().pipe(
  catchError(err => this.handleError(err)
)

What's the difference and why is the snackBar instance undefined?

Upvotes: 2

Views: 1879

Answers (1)

Mrk Sef
Mrk Sef

Reputation: 8022

The problem

The issue is that a when a function is called on an object, the object is implicitly passed as that's functions context (its this). Sometimes you'll see this invocation style called a method rather than a function.

Methods are implicitly given the object whose properties they're allowed to operate on. You can, however, make this relationship explicit.

this.handleError, doesn't call the function, it just passes a reference to it. Now catchError doesn't know that it needs to call this.handleError(err) instead of handleError(err).

In general, if a function uses the this keyword, then the caller is responsible for supplying the this context. Using dot notation makes this pretty intuitive but causes strange behaviour in the corner cases.


Solution:

One fix is to use a lambda function to do that step for you. Lamda function (using arrow => syntax) always retain the context relative to where they are defined. So in this case, this refers to what you expect.

Another solution is to bind the right object to the function call (which lets you call the function as a method on the bound object). This workaround is more robust in some cases as it lets you bind any context to a function. In this case, however, you're just binding the current context.

like this:

constructor(private snackBack: MatSnackBar) {
...
this._obv$ = this.myService.getTypes().pipe(
  catchError(this.handleError.bind(this))
)
...

Upvotes: 3

Related Questions