Hi there.
I’m trying to send a standard GET request to an API that returns some categories as json data. The server is secured with basic auth, so I have to manually create the request options. This takes some time, so I chose to implement an Observable that returns the request options and completes after that.
However, when executing I get
Uncaught (in promise): TypeError: Cannot read property ‘subscribe’ of undefined
Call to Server in categoryService:
getCategories() : any {
console.log("Getting categories from: " + this.serviceUrl);
// creating the request
let url = this.serviceUrl + "category/appCategories";
// getting request options
this.userService.getRequestOptionsForLoggedInUser().subscribe(opt => {
// sending request to server
// returning observable to subscribe to
return this.http.get(url, opt)
.map(res => res.json());
});
}
Calling the function:
this.categoryService.getCategories().subscribe(categories => {
console.log("Fetched categories from server: ", categories);
}
My guess: getCategories() is completed before the request is sent, therefore it does not return an Observable to subscribe to.
Any ideas on how to fix this? Should I use Promises instead of Observables?
Can you show what this function looks like? The problem is that getCategories itself doesn’t actually return anything, but I feel like you’re doing some unnecessary stuff, so before I provide sample code I’d like to see the code for the getRequestOptionsForLoggedInUser
getRequestOptionsForLoggedInUser(): Observable<RequestOptions> {
return Observable.create(observer => {
let obs = observer;
this.getCredentials().subscribe(user => {
// this just encodes the user data to base64
let auth = this.baseEncodeUserCredentials(user);
// building the header
let opt: RequestOptions;
let myHeaders: Headers = new Headers
myHeaders.append('Authorization', 'Basic ' + auth);
opt = new RequestOptions({ headers: myHeaders });
obs.next(opt);
obs.complete();
});
});
}
getCredentials(): Observable<User> {
return Observable.create(observer => {
let obs = observer;
this.storage.get('username').then(username => {
this.storage.get('password').then(password => {
let user: User = new User(username, password)
obs.next(user);
obs.complete();
});
});
});
}
User Data is stored locally.
It worked fine before I changed getRequestOptionsForLoggedInUser to return an observable. Before that it returned RequestOptions with hard coded user data and not from storage. Since I get the name and password from storage now, I had to change it to wait until the data is there, otherwise it would create RequestOptions with an empty authentification.
Yeah you should use Promises for some of these, here’s my approach. It’s a bit … clumsy, so to say, because you’re wanting to return the Observable from the HTTP call within a Promise (or what was previously an Observable). I couldn’t find a way for it to simply return the Observable, I might be too tired…
Bear in mind I didn’t test this! Just quickly wrote it up.
getRequestOptionsForLoggedInUser(): Promise<RequestOptions> {
return new Promise((resolve, reject) => {
this.getCredentials().then(
user => {
// this just encodes the user data to base64
let auth = this.baseEncodeUserCredentials(user);
// building the header
let opt: RequestOptions;
let myHeaders: Headers = new Headers
myHeaders.append('Authorization', 'Basic ' + auth);
resolve(new RequestOptions({ headers: myHeaders }));
},
err => {
reject(err);
}
);
});
}
btw. it is not necessary to double wrap already promises in new Promise. Simply return the success value or use “throw” to raise a custom exception, which leads to reject.