mikewagz mikewagz - 3 months ago 77
TypeScript Question

Use promise for headers in Angular 2 HTTP client wrapper

I'm having trouble understanding how to refactor this Angular 2 HTTP wrapper so that my headers are loaded from storage before the request.

I am currently loading up the headers in the constructor, but sometimes then retrieval from storage doesn't complete before the request goes out. So far, I haven't had luck moving the header formation into a promise but still returning the Observable request object. Instead the Promise was returned, which I would have to then() to get to the Observable.

Any guidance is appreciated!

import { Injectable } from '@angular/core';
import { Http, Headers, Response, HTTP_PROVIDERS } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { Storage, SqlStorage } from 'ionic-angular';


@Injectable()
export class HttpClient {
headers : Headers;

constructor(private http: Http) {

this.headers = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8'
})

// Add auth token to headers
let storage = new Storage(SqlStorage);

storage.get("user").then(
(u) => {
if(u){
let user = JSON.parse(u);
this.headers.append('X-User-Email', user.email);
this.headers.append('X-User-Token', user.authentication_token);
}else{
console.log("User not found in storage.")
}
}
)
}

get(url) {
console.log("GET headers: ", this.headers)

// I want to add a promise here to load the header data
// from storage before sending this request. I still
// want to return this same Observable http request.

return this.http.get(url, {
headers: this.headers
})
}
}


EDIT

Here's my attempt, which returns
'Promise<Observable<Response>>'
instead of
Observable
:

import { Injectable } from '@angular/core';
import { Http, Headers, Response, HTTP_PROVIDERS } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { Storage, SqlStorage } from 'ionic-angular';

/*
A custom wrapper to include headers for API authentication.
Header info is stored in local storage.
*/

@Injectable()
export class HttpClient {

constructor(private http: Http) {
}

buildHeaders(){
let headers = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8'
})
let storage = new Storage(SqlStorage);

return storage.get("user").then(
(u) => {
if(u){
let user = JSON.parse(u);
headers.append('X-User-Email', user.email);
headers.append('X-User-Token', user.authentication_token);
return headers;
}
}
)
}

get(url) {
return this.buildHeaders().then(
(headers) => {
return this.http.get(url, {
headers: headers
})
}
)
}
}


Here is the caller receiving the error:

this.http.get("http://localhost:3000/confirm_auth_token")
.subscribe(
data => console.log(data)
)


Which results in:

Error TS2339: Property 'subscribe' does not exist on type 'Promise<Observable<Response>>'.


How can I "unwrap" that promise within the get() method of the HttpClient?

Answer

Your HttpClient.get function is returning a Promise, so you need a then to access the Observable<Response> when the Promise resolves:

this.http
    .get("http://localhost:3000/confirm_auth_token")
    .then((observable) => {
        observable.subscribe((response) => { console.log(response.text()); }); // or response.json() or whatever
    });

If you want to return an Observable from your get function, you can rearrange things like this:

get(url) {
    return Observable
        .fromPromise(this.buildHeaders())
        .switchMap((headers) => this.http.get(url, { headers: headers }));
}

Your get call would then be something like this:

this.http
    .get("http://localhost:3000/confirm_auth_token")
    .subscribe(response => { console.log(response.text()); }); // or response.json() or whatever
Comments