Angular2 http.get() ,map(), subscribe() and observable pattern - basic understanding
Here is where you went wrong:
this.result = http.get('friends.json')
.map(response => response.json())
.subscribe(result => this.result =result.json());
it should be:
http.get('friends.json')
.map(response => response.json())
.subscribe(result => this.result =result);
or
http.get('friends.json')
.subscribe(result => this.result =result.json());
You have made two mistakes:
1- You assigned the observable itself to this.result
. When you actually wanted to assign the list of friends to this.result
. The correct way to do it is:
-
you subscribe to the observable.
.subscribe
is the function that actually executes the observable. It takes three callback parameters as follow:.subscribe(success, failure, complete);
for example:
.subscribe(
function(response) { console.log("Success Response" + response)},
function(error) { console.log("Error happened" + error)},
function() { console.log("the subscription is completed")}
);
Usually, you take the results from the success callback and assign it to your variable.
the error callback is self explanatory.
the complete callback is used to determine that you have received the last results without any errors.
On your plunker, the complete callback will always be called after either the success or the error callback.
2- The second mistake, you called .json()
on .map(res => res.json())
, then you called it again on the success callback of the observable.
.map()
is a transformer that will transform the result to whatever you return (in your case .json()
) before it's passed to the success callback
you should called it once on either one of them.
Concepts
Observables in short tackles asynchronous processing and events. Comparing to promises this could be described as observables = promises + events.
What is great with observables is that they are lazy, they can be canceled and you can apply some operators in them (like map
, ...). This allows to handle asynchronous things in a very flexible way.
A great sample describing the best the power of observables is the way to connect a filter input to a corresponding filtered list. When the user enters characters, the list is refreshed. Observables handle corresponding AJAX requests and cancel previous in-progress requests if another one is triggered by new value in the input. Here is the corresponding code:
this.textValue.valueChanges
.debounceTime(500)
.switchMap(data => this.httpService.getListValues(data))
.subscribe(data => console.log('new list values', data));
(textValue
is the control associated with the filter input).
Here is a wider description of such use case: How to watch for form changes in Angular 2?.
There are two great presentations at AngularConnect 2015 and EggHead:
- Observables vs promises - https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises
- Creating-an-observable - https://egghead.io/lessons/rxjs-creating-an-observable
- RxJS In-Depth https://www.youtube.com/watch?v=KOOT7BArVHQ
- Angular 2 Data Flow - https://www.youtube.com/watch?v=bVI5gGTEQ_U
Christoph Burgdorf also wrote some great blog posts on the subject:
- http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
- http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
In action
In fact regarding your code, you mixed two approaches ;-) Here are they:
-
Manage the observable by your own. In this case, you're responsible to call the
subscribe
method on the observable and assign the result into an attribute of the component. You can then use this attribute in the view for iterate over the collection:@Component({ template: ` <h1>My Friends</h1> <ul> <li *ngFor="#frnd of result"> {{frnd.name}} is {{frnd.age}} years old. </li> </ul> `, directive:[CORE_DIRECTIVES] }) export class FriendsList implement OnInit, OnDestroy { result:Array<Object>; constructor(http: Http) { } ngOnInit() { this.friendsObservable = http.get('friends.json') .map(response => response.json()) .subscribe(result => this.result = result); } ngOnDestroy() { this.friendsObservable.dispose(); } }
Returns from both
get
andmap
methods are the observable not the result (in the same way than with promises). -
Let manage the observable by the Angular template. You can also leverage the
async
pipe to implicitly manage the observable. In this case, there is no need to explicitly call thesubscribe
method.@Component({ template: ` <h1>My Friends</h1> <ul> <li *ngFor="#frnd of (result | async)"> {{frnd.name}} is {{frnd.age}} years old. </li> </ul> `, directive:[CORE_DIRECTIVES] }) export class FriendsList implement OnInit { result:Array<Object>; constructor(http: Http) { } ngOnInit() { this.result = http.get('friends.json') .map(response => response.json()); } }
You can notice that observables are lazy. So the corresponding HTTP request will be only called once a listener with attached on it using the subscribe
method.
You can also notice that the map
method is used to extract the JSON content from the response and use it then in the observable processing.
Hope this helps you, Thierry
import { HttpClientModule } from '@angular/common/http';
The HttpClient API was introduced in the version 4.3.0. It is an evolution of the existing HTTP API and has it's own package @angular/common/http. One of the most notable changes is that now the response object is a JSON by default, so there's no need to parse it with map method anymore .Straight away we can use like below
http.get('friends.json').subscribe(result => this.result =result);