Angular2- Getting confused with Observable Catch c

2020-03-01 06:46发布

问题:

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

回答1:

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