Angular HTTP calls: promise, promise chaining and its simplification with Rxjs

Hi there! Today I am going to talk about a very specific scenario we face while creating Angular service.

If you think you think you are aware of the initial information, and quickly want to jump to the main point with example, here is the link to the section.

So to start with, let’s say when you want to make an HTTP call from your Angular controller, you would do like this.


// controller function – calling service
$scope.loadOrders = function() {
orderService.loadOrder()
.then(function(result){
if(result) {
//populate $scope property for the view
}
})
}


svc.loadOrders = function() {
return $http.get('<loadOrderURL>'); // this resturns a promise
}

view raw

orderService.js

hosted with ❤ by GitHub

In this example, we are loading orders related data in orderController from ‘orderService’.

Now, these orders might be different to every user. We only what to show the orders to the users which belong to the him/her. At server we need to identify for which user the request is coming. We can anyways pass that info as part of the payload for the server to identify.

But that’s not how you should do, not the standard way! As the user related details is actually not for the call ‘loadOrders’,  we should pass it in message header part, like example below.


svc.loadOrders = function() {
var headers = {
'userId': '<userId>',
'license': '<licenseKey>'
};
return $http.get('loadOrderURL', headers);
}

view raw

orderService.js

hosted with ❤ by GitHub

Okay, got it, but this way we would end up putting the same code in each and every service function. Can we avoid this?

Yes, there is a better way.

We can create our own custom service, which does this additional work, and let our orderService work only for those things for which it is designed for. ‘Separation of Concerns’


angular.module('myModule')
.service('myAuthSvc', ['$http', '$q', function($http, $q){
var loggedInUserDetails = null;
this.getLoggedInUserDetails = function(payload) {
var deferred = $q.defer();
if(loggedInUserDetails != null) { deferred.resolve(loggedInUserDetails); }
else{
$http.post('<loginURL>', /*payload for login*/)
.then(function(result){
loggedInUserDetails = result;
deferred.resolve(result);
});
}
return deferred.promise;
}
}]);

view raw

myAuthSvc.js

hosted with ❤ by GitHub


angular.module('myModule')
.service('myHTTPSvc', ['$http', '$q', 'myAuthSvc', function($http, $q, myAuthSvc){
this.get = function(url, payload, header) {
var deferred = $q.defer();
myAuthSvc.getLoggedInUserDetails()
.then(function(data){
var loggedInUserDetails = data;
var headers = {
'license': loggedInUserDetails.licenseInfo
};
//here you make the actual HTTP call
$http.get(url, payload, headers)
.then(function(result){
deferred.resolve(result);
}, function(error){
deferred.reject(error);
});
});
return deferred.promise;
}
}]);

view raw

myHTTPSvc.js

hosted with ❤ by GitHub


angular.module('myModule')
.service('orderSvc', ['myHTTPSvc', function(myHTTPSvc){
this.loadOrders = function(payload) {
// myHTTPSvc will add required headers for the HTTP server
return myHTTPSvc.get('<loadOrderURL>', payload);
}
}]);

view raw

orderService.js

hosted with ❤ by GitHub

Here, myHTTPSvc calls myAuthSvc to fetch logged-in user details, these details are used by myHTTPSvc to push those details inside headers. orderService is just calling myHTTPSvc and get its things done.

Have you observed $q.defer part? In myHTTPSvc.get function, we are dealing with an Async (the actual get) calls which is dependent another one (myAuthSvc.getLoggedInUserDetails). That’s where deferred.resolve comes into picture. So when we receive the result we are expecting, doing deferred.resolve(data) calls the success callback from the controller. You can read more about $q service at AngularJS Documentation for $q

Let’s talk about the same kind of scenario for Angular 2+. How we’d do as we don’t have $q service in Angular 2+?

That’s where Rxjs/AsyncSubject comes into picture. Have a look at the example below. It’s pretty easy and self explanatory.


import { Http, Headers, RequestOptions } from '@angular/http';
import { AsyncSubject } from 'rxjs/AsyncSubject';
@Injectable()
export class myHTTPSvc {
constructor(private http: Http, private upSvc: MyUserProfileService) {}
get(url) : Observable<any> {
let subjectResult = new AsyncSubject();
this.upSvc.getLoggedInUserDetails()
.subscribe(details => {
let headerConfig = this.getHeaderConfig(details);
// here is the actual GET call you want to make
this.http.get(url, headerConfig)
.map(data => data.json())
.subscribe(result => {
// similar to deferred.resolve(result) we used to do with Angular 1.x
subjectResult.next(result);
subjectResult.complete();
});
});
return subjectResult;
}
private getHeaderConfig(loggedInUserDetails: any): RequestOptions {
let options = new RequestOptions();
options.headers = new Headers();
options.headers.append('license', loggedInUserDetails.licenseInfo);
// or any details you want to push in message header
return options;
}
}

view raw

myHTTPSvc.ts

hosted with ❤ by GitHub

If you feel like digging more into Subject, AsyncSubject, do visit  GitHub or ReactiveX documentation.

Still not happy? You want more simplicity in the implemented get method?

Here comes Rxjs.Observable.forkJoin and .map!


import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class myHTTPSvc {
constructor(private http: Http, private upSvc: MyUserProfileService) {}
get(url) : Observable<any> {
this.upSvc.getLoggedInUserDetails()
.flatMap(details => {
let headerConfig = this.getHeaderConfig(details);
return this.http.get(url, headerConfig)
.map(data => data.json());
});
}
private getHeaderConfig(loggedInUserDetails: any): RequestOptions {
let options = new RequestOptions();
options.headers = new Headers();
options.headers.append('license', loggedInUserDetails.licenseInfo);
// or any details you want to push in message header
return options;
}

view raw

myHttpSvc.ts

hosted with ❤ by GitHub

The advantage of using .flatMap operator is that when we subscribe to get function of myHttpSvc from our component code, that will be subscribed for the Observable which is being returned by this.http.get function.

So precise and simple, it’s it?

There are many operators to explore for Rxjs and they all are worth to know!