2016-03-25 48 views
14

Chiedersi se si può dare un po 'di assistenza. Mi sembra di essere un po 'confuso quando si tratta di utilizzare catch con Observable s.Angolare2- Come confondersi con il campo di chiusura Observable Catch

Fondamentalmente quello che sto cercando di fare è il seguente: Quando il mio API restituisce un errore 403, voglio eseguire alcune azioni sul mio TokenStore, vale a dire, eliminare il token locale e contrassegnare l'utente come non autenticato. Il modo in cui sto provando a farlo potrebbe essere sbagliato, quindi per favore fatemi sapere se c'è un modo migliore per farlo.

Sto cercando di ottenere questo risultato con il seguente codice:

APIConnector.service.ts - un unico servizio per i metodi di comunicazione API

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 ognuno dei miei servizi che utilizzano il APIConnector, ho metodi di cattura su Observable s, per eseguire la chiusura handleError. per esempio.

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

Tuttavia, questo dà il seguente errore:

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

Presumibilmente questo è perché handleError è una chiusura. Non sono sicuro del modo migliore per affrontare questo, però.

Ogni pensiero sarebbe molto apprezzato

+2

Che cosa succede se si fa '.catch (errore => this._apiConnector.handleError (errore)) '? – Abdulrahman

risposta

21

Il problema è che si fa riferimento alla funzione direttamente in modo da perdere il suo contesto. Voglio dire, è ora un problema e un metodo.

Ci sono due modi per risolvere questo:

  • vincolanti la funzione a questo:

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

    Questo approccio non è consigliabile perché si perde tipi qui. Vedere questo link per maggiori informazioni: https://basarat.gitbooks.io/typescript/content/docs/tips/bind.html

  • avvolgendo la chiamata in una funzione di freccia:

    .catch((error) => { 
        this._apiConnector.handleError(error); 
    }); 
    
+4

Questa risposta era corretta, con una piccola regolazione. Il metodo catch richiede che tu restituisca qualcosa, quindi è necessario un 'return' tra parentesi. – user43138

+2

Si restituisce qualcosa se si desidera lanciare un nuovo errore o lo stesso: "return Observable.throw (error);". Se non è il tuo caso, non tornare. L'errore non verrà propagato a un'altra cattura o alla seconda richiamata di subscribe. –

+2

Ho avuto lo stesso problema e questa risposta l'ho risolto. Comunque mi piace la soluzione senza parentesi e senza '_apiConnector' molto meglio '.catch (error => this.handleError (error));' – hogan