Angular2 Http with RxJS Observables
Subscribing has never been so easy
If you have been following the development efforts of the Angular2 project, you have witnessed certain highs and lows - but it has been a fun ride. The latest version is only a Release Candidate and the team
is getting closer to the final release. I’m really looking forward to that! I wanted to take a moment to highlight (IMO) one of the key services of Angular2, the http
service.
In AngularJs 1 the ng.IHttpService
(aka, $http
) was based on
promises and deferrals.
In Angular2 we now rely on RxJS and the observable pattern. In my opinion this is a huge win!
If you’re unfamiliar with Reactive Extensions in general, I suggest starting here.
RxJS is the JavaScript
implementation of Reactive Extensions.
Let’s take a moment to compare and contrast the two, and immerse ourselves in the wonderful world of RxJS.
Let me be clear upfront that I’m developing with TypeScript
.
Legacy Pattern
As a developer you would have to use both the ng.IHttpService
and the ng.IQService
in combination to collaborate the deferral of the
HTTP request and the promise that represented it. Consider the following:
module ExampleModule {
export class ExampleService implements IExampleService {
static $inject = ["$http", "$q"];
private $http: ng.IHttpService;
private $q: ng.IQService;
constructor($http: ng.IHttpService,
$q: ng.IQService) {
this.$http = $http;
this.$q = $q;
}
public getFooBars(): ng.IPromise<FooBar[]> {
var deferred = this.$q.defer<FooBar[]>();
this.$http
.get("api/foobar")
.success((data) => {
deferred.resolve(data);
})
.error((error) => {
console.log("An error occurred when requesting api/foobar.", error);
deferred.reject(error);
});
return deferred.promise;
}
}
}
In this example, we can easily see the interaction betwixt the ng.IHttpService ($http)
and ng.IQService ($q)
services. The $q
variable exposes a .defer<T>
function that returns a deferred object.
Deferred API
Function | Parameters | Description |
---|---|---|
resolve |
(value: T) |
Resolved yielding the materialized value of type T |
reject |
(reason: string) |
Rejected with the given reason |
The deferred object instance is passed into the fluent API’s of the $http's
.success
and .error
functions accordingly. This pattern works great, but is very limiting and repetitive.
You end up writing a lot of boilerplate code and that isn’t very DRY. Let’s look at how this is approached with Angular2’s http
service using the observable pattern from RxJS!
New Pattern
To be fair, let’s implement the same functionality and public surface-area such that our example services are equivalent.
import {Observable} from "RxJS/Rx";
import {Injectable} from "@angular/core";
import {Http} from "@angular/http";
@Injectable() export class ExampleService {
constructor(private http: Http) { }
getFooBars(onNext: (fooBars: FooBar[]) => void) {
this.http
.get("api/foobar")
.map(response => response.json() as FooBar[])
.subscribe(onNext,
error =>
console.log("An error occurred when requesting api/foobar.", error));
}
}
I am hoping that you noticed how much cleaner this code is, as well as how much more readable!
Now, I know what you’re thinking…these cannot possibly be the same examples, but they are in fact doing the same thing. Dependency Injection (DI) in Angular2 is a lot less error prone (no more magic strings) and way easier than
it was in AngularJs 1.
Simply do what you’d expect from any other common constructor-based DI framework, ensure that your desired Http
type is registered as a provider to the system. This happens by way of the HTTP_PROVIDERS
defined in our boot.js
file.
More on that in another post. With modern TypeScript
we can define properties and fields, and their corresponding access modifiers right from within our constructors.
Syntax Tip
This exemplifies the comparisons in syntax between a simple constructor and the more verbose constructor.
// Simple .ctor()
constructor(private http: Http) { }
// Is equivalent to...
private http: Http;
constructor(http: Http) {
this.http = http;
}
Likewise, the following is true regarding public access modifiers.
// Simple .ctor()
constructor(http: Http) { }
// Is equivalent to...
http: Http; // When the access modifier is omitted it's defaulted to public
constructor(http: Http) {
this.http = http;
}
Comparing the APIs
Instead of the .success
invocation with a corresponding deferred.resolve
call, we now utilize the RxJS .map
and .subscribe
operators. Let’s look at these below:
Operator | Description |
---|---|
map |
Transform the items emitted by an Observable by applying a function to each item |
subscribe |
The Subscribe operator is the glue that connects an observer to an Observable |
Mapping is easy and we can leverage some of the TypeScript
language features to cast the JSON blobs returned from our services as strongly typed objects. The map
operator is actually
synonymous with the select
operator, so if you’re more familiar with that terminology you can use it interchangeably.
Advantages
Now that we have an understanding of how RxJS compares to the legacy pattern, we can take advantage of all the various benefits. Imagine with me that we have a need to implement retry logic,
this would have been challenging with the legacy pattern but with the new pattern it’s as simple as saying .retry
. Consider the following:
// If this call fails, we'll try it again with the same payload two times
getFooBars(onNext: (fooBars: FooBar[]) => void) {
this.http
.get("api/foobar")
.map(response => <FooBar[]>response.json())
.retry(2)
.subscribe(onNext,
error =>
console.log("An error occurred when requesting api/foobar.", error));
}
Now imagine a scenario where a user is typing and you want to provide an autocomplete, you could use .debounce
to pause for a brief moment prior to sending the request. Likewise,
we could apply a .filter
that only takes action when a certain number of characters have been entered. Finally, we might utilize .distinctUntilChanged
to only execute the request once
the values are actually different than they once were.
You could take advantage of .buffer
, .throttle
, .interval
, .window
, .range
, etc… The list goes on and on,
and
this is the source for most of what you can take advantage
.
Let’s Summarize
Angular2 has a new implementation of their http
service that relies on RxJS. The API uses observables
and the observer
pattern to allow for a fluent experience that is rich and robust.
Getting started is straight-forward and simple. Before too long you’ll be taking advantage of the feature-full set of Reactive Extensions and thinking in terms of data streams.
This mindset will make your life easier - trust me!
Further Reading
Share this post
Sponsor
Twitter
Facebook
Reddit
LinkedIn
StumbleUpon
Email