Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2- Getting confused with Observable Catch closure scope

Wondering if you can give a little assistance. I appear to be getting myself a bit confused when it comes to using catch with Observables.

Basically what I'm trying to do is the following: When my API returns a 403 error, I want to perform some actions on my TokenStore, namely, delete the local token and mark the user as unauthenticated. The way I'm trying to do this may be wrong, so please do let me know if there's a better way of accomplishing this.

I'm trying to accomplish this with the following code:

APIConnector.service.ts - a single service for API communication methods

import {Injectable} from 'angular2/core';
import {Http, Response, Headers, RequestOptions} from 'angular2/http';
import {Observable}     from 'rxjs/Observable';
import * as _ from 'lodash';
import {Logger}     from './logger.service';
import {TokenStore} from '../stores/token.store';

@Injectable()
export class APIConnector {

    private _apiUrl:string = 'https://api.sb.zerojargon.com/';
    private _token:string = null;

    constructor(
        private _http:Http,
        private _logger:Logger,
        private _tokenStore:TokenStore
    ) {}

    get(endpoint:String, includes:Array<string>) {
        let includeString = (!_.isUndefined(includes)) ? this._parseIncludes(includes) : '';
        let headers = this._createHeaders();
        let options = new RequestOptions({ headers: headers });
        return this._http.get(this._apiUrl + endpoint + '?include=' + includeString, options);
    }

    post(endpoint:String, formData:Object, includes:Array<string>) {
        let includeString = (!_.isUndefined(includes)) ? this._parseIncludes(includes) : '';
        let body = JSON.stringify(formData);
        let headers = this._createHeaders();
        let options = new RequestOptions({ headers: headers });
        return this._http.post(this._apiUrl + endpoint + '?include=' + includeString, body, options);
    }

    handleError(error: Response) {
        // log out the user if we get a 401 message
        if (error.json().error.http_code === 401) {
            this._tokenStore.destroy();
        }
        return Observable.throw(error.json().error || 'Server error');
    }

    private _parseIncludes(includes:Array<String>) {
        return includes.join();
    }

    private _createHeaders() {
        return new Headers({ 'Content-Type': 'application/json', 'Authorization': 'bearer ' + localStorage.getItem('token') });
    }
}

In each of my services which use the APIConnector, I have catch methods on the Observables, to run the handleError closure. e.g.

public createEvent(event:Object) {
    let endpoint = this._endpoint;
    return this._apiConnector.post('clients/'+this.client+'/events', event, this._defaultIncludes)
        .map(res => {
            return this._transformer.map('event', <Object[]>res.json());
        })
        .catch(this._apiConnector.handleError);
}

However, this gives the following error:

EXCEPTION: TypeError: Cannot read property 'destroy' of undefined

Presumably this is because handleError is a closure. I'm unsure of the best way to deal with this, though.

Any thoughts would be greatly appreciated

like image 840
user43138 Avatar asked Mar 25 '16 21:03

user43138


People also ask

Why observable is lazy?

Observables are "lazy", meaning if no one is listening, nothing happens.

Can you subscribe to observable multiple times?

In your component's template file, you can subscribe to this observable using the async pipe. The async pipe handles the subscribe/unsubscribe events for you, so you no longer need onInit and onDestroy in your component's TS file. And that's it!

How do you know if observable has value?

The RxJS isEmpty() operator returns an observable with a Boolean value indicating whether the observable was empty or not. It returns an output as true if the source observable is empty; otherwise, false.

What is observable in Angular 2?

Observables provide support for passing messages between parts of your application. They are used frequently in Angular and are a technique for event handling, asynchronous programming, and handling multiple values.


1 Answers

The problem is that you reference the function directly so you lose its context. I mean it's now afunction and a method.

There are two ways to fix this:

  • binding the function to this:

    .catch(this._apiConnector.handleError.bind(this));
    

    This approach isn't recommended because you will lose types here. See this link for more details: https://basarat.gitbooks.io/typescript/content/docs/tips/bind.html

  • wrapping the call into an arrow function:

    .catch((error) => {
      this._apiConnector.handleError(error);
    });
    
like image 125
Thierry Templier Avatar answered Sep 27 '22 20:09

Thierry Templier