How do I use observable and observer in angular? - javascript

I come from a synchronous programming background and I am having a hard time understanding observables.
Here is an extract of my service/provider (Ionic 2 project)
return this.http.get(`${this.serverApi}`)
.map(res => <Response[]>res.json());
and I will subscribe to that from the LoginPage. I have several questions regarding this.
Does the above code return an observable/ observer even if I didn't declare it as such?
The response is JSON. How do I do some checking/processing of the JSON and perform some action like if
res.auth_token==true
then do
localStorage.setItem(res.auth_token)
I believe it should be done in the provider class. Just a typical hint/example will be awesome.
Does the request actually happen when it hits the subscribe method?
Creating and returning Observable from Angular 2 Service mentions Subject and ReplaySubject. Should I use them instead?

The code will return an Observable
You can change the callback body to a block and add as much code as you want
return this.http.get(`${this.serverApi}`)
.map(res => {
let x = <Response[]>res.json();
// do something with x
res.auth_token == true;
return res; // with a block body an explicit`return` is required
});
Yes, calling .subscribe() will execute the http.get() and then all subsequent operators when the response arrives. Without .subscribe() nothing will happen.
Note: Some Angular APIs expect an Observable, in this case you must not call subscribe() because subscribe() returns a Subscription, not an Observable.
it depends what you try to accomplish. If you use http.get(), you already get an observable and there is no need for Subject, ReplaySubject, or any of the other possibilities.

Related

Call firebase httpsCallable multiple times

Calling foo two times creates two subscribers and that is not ok but even so the second call of foo seems to get stuck somewhere because console.log gets called only one time.
I know that I should not subscribe in this manner but I really do not see the solution to get myFunc to output the second result
const myFunc = functions.httpsCallable('myFunc')
function foo(inData){
myFunc({data:inData}).subscribe((res)=>{
console.log(res)
})
}
foo('aaaaa')
foo('bbbbb')
second attempt still no luck
myFunc(data){
return functions.httpsCallable('myFunc')(data)
}
async function foo(inData){
let res = await lastValueFrom(myFunc({data:inData}))
console.log(res)
}
foo('aaaaa')
foo('bbbbb')
The HttpsCallable function returns a promise by default. As you can see in this related question , there are a few differences between promises and observables, as it seems that you are already working with observables, I would try to convert the promise received from the function to an observable using this method, as suggested in this other question:
import { from } from 'rxjs';
const observable = from(promise);
Although, there are other methods suggested as well in the same question.
I would also like to point you to the Using observables to pass values, I think you might not have shared how you are creating the observer. I'm just guessing but you should also unsubscribe to clean up data ready for the next subscription. Also, missing next notification that is required.
As an extra, I would say that if you are not obligated to use observables, handle it as a promise.

Returning a nested observable

Given this method:
public logIn(data:any): Observable<any> {
this.http.get('https://api.myapp.com/csrf-cookie').subscribe(() => {
return this.http.post('https://api.myapp.com/login', data);
});
}
I would like it to return that nested observable, so that my calling code can use it like so:
this.apiService.logIn(credentials).subscribe(() => {
// redirect user to their dashboard
});
without needing to know about the first /csrf-cookie request. Obviously the above doesn't work - but I'm struggling to understand how to make the inner HTTP request wait for the outer one to finish AND be returned by the method.
you should use switchMap see the documentation on switch map
public logIn(data:any): Observable<any> {
return this.http.get('https://api.myapp.com/csrf-cookie').pipe(
switchMap(x => this.http.post('https://api.myapp.com/login', data))
);
}
with rxjs nested subscribes are generally not a good idea. There are many great operators within the library that will get you around it. In this case above where one call depends on another switchMap(...) is the best fit.
Also the code has been modified to return the observable not the subscription

Converting Observable to Promise

Is it a good practice to convert the observable object to a promise since observable can be used in almost all the occasions instead of promise?
I've started to learn angular recently and come across the below code snippet in a new project(Angular 5) at my workplace. This code snippet is used to load a list of data such as a customer list. This customer list data set is received as a one time action, not as a stream. Therefore it has no technical limitation to use promise. But I would like to know whether there are any drawbacks or limitations.
getViewDataForPage(): Promise<any> {
return this.commonDataService.getViewDataForPage(args_set)
.toPromise()
.catch(error => this._exceptionService.catchBadResponse(error));
}
//in commonDataService.ts
getViewDataForPage(args_set): Observable<any> {
/** logic goes here */
return this.httpConnection.post(viewDataRequest, args);
}
It depends on your requirement, technically observables are better than promises because they provide the features of Promise and more. With Observable, it doesn't matter if you want to handle none to multiple events.
Observables are cancelable ie., using unsubscibe() you can cancel an observable irrespective of its state.
Promises on the other hand deal with only 1 async event ie.., either it will resolve or reject if error occurs
A good place for Promise would be if you have a single even to process for example.
let connect=new Promise((resolve,reject)=>{
if( connection Passed){
resolve("Connected");
} else{
reject("failed");
}
}
You can use both. The Observable is used when you are going to receive different values during the time is more like when you are a subscriptor in a some magazine, when ever the mazagazine has new edition you will be notifier and the same happens to all subscriptors and everybody receives the new value or in this case the new magazine.
In the case of the Promise it is Async like an observable but it just happen ones.
Then if the following code will happens ones in this case is ok the promise
getViewDataForPage(): Promise<any> {
return this.commonDataService.getViewDataForPage(args_set)
.toPromise()
.catch(error => this._exceptionService.catchBadResponse(error));
}
//in commonDataService.ts
getViewDataForPage(args_set): Observable<any> {
/** logic goes here */
return this.httpConnection.post(viewDataRequest, args);
}

RxJs - (why) Does an Observable emit it's latest value on a new subsription?

Ok, this is a quick one, i'm kinda exhausted already and am confusing myself :D
I'm working with angular2 and RxJS Observables.
I have a service with a property "data", which is an Observable that get's set in the constructor, and a method to return this observable to subscribe to.
export class test{
private data: Observable<Object>
constructor(private http: Http){
this.data = this.http.get(url).map( res => res.json());
}
getData(): Observable<Object>{
return this.data
}
}
I have worked wit replaySubjects a while ago to always emit all values of the sequence to new subscribers. However, with the code above the Observable seems to emit it's latest value to new subscribers. Is this intended?
test(i: number) {
if (i > 0) {
setTimeout( () => {
this.dataService.getData().subscribe( (data) => {
this.debug.log(data);
this.test(i-1);
});
}, 2000);
}
}
test(4)
I get a value for every iteration. I am confused, 'cause a year ago when i wanted that behaviour, i got no new values when subscribing 'too late'.
Essentially, i just want to cache the result of the http.get, and deliver the same value to all subscribers, instead of making a new http request for every subscription (returning the http.get(url).. in getData())
I know this question is a bit old, but the answers seem to me quite confusing.
The Observable you return from the method getData() is just that, an Observable. So every time a consumer subscribes it gets the response. So it is working fine, but it is indeed making a new request every time.
In order to cache the result there are plenty of ways to do it depending on the behavior you want. To just cache a single request I would recommend t use the #publishBehavior operator:
export class test{
private data: Observable<Object>;
constructor(private http: Http){
this.data = this.http.get(url)
.map(res => res.json())
.publishBehavior([])
.refCount();
}
getData(): Observable<Object>{
return this.data;
}
}
The parameter passed to the publishBehavior is the initial value. With this two operators, the request will be made when the first subscriber arrived. Next subscribers will get the cached answer.
In others answers the use of Subjects has been suggested. The publishBehavior is using subjects under the hood. But to directly call next() it is consider bad practice unless there is no other remedy, and thats not the case for this scenario in my opinion. Even if you use Subjects directly, it will be wise to return an Observable to the Components by using the #asObservable() operator so the component won't have access to the next, error and complete methods.
No. You need to use Subject for this. It has a method next() to which you will send your newly arrived property so that it pushes it to the subscribers.
In addition to this, you should create a service that will be a singleton. Whenever your components instantiate it in a constructor, they will receive the object already formed with all the data. There will be no need to fetch the data every time.
Also, instead of instantiating your data in the constructor, implement OnInit and do the calls to the server from there.

Hot and shared Observable from an EventEmitter

Is there a way to have a hot observable from an EventEmitter (or equivalent available in Angular 2 alpha 46 / RxJS 5 alpha)? i.e. if we subscribe after the value is resolved, it triggers with the previously resolved value. Similar to what we have when always returning the same promise.
Ideally, only using Angular 2 objects (I read somewhere a light RxJS would be embedded later to remove the dependency), otherwise importing RxJS is fine. AsyncSubject seems to match my need, but it is not available in RxJS 5 alpha.
I tried the following, without success (never triggers). Any idea about how to use it?
let emitter = new EventEmitter<MyObj>();
setTimeout(() => {emitter.next(new MyObj());});
this.observable = emitter;
return this.observable.share();
Full plunker here comparing hot and cold
Usecase: reach some async objects only once (for example a series of HTTP calls merged/wrapped in a new EventEmitter), but provide the resolved async object to any service/component subscribing to it, even if they subscribe after it is resolved (the HTTP responses are received).
EDIT: the question is not about how to merge HTTP responses, but how to get a (hot?) observable from EventEmitter or any equivalent available with Angular 2 alpha 46 / RxJS 5 alpha that allows to subscribe after the async result is retrieved/resolved (HTTP is just an example of async origin). myEventEmitter.share() does not work (cf plunker above), although it works with the Observable returned by HTTP (cf plunker from #Eric Martinez). And as of Angular 2 alpha 46, .toRx() method does not exist any more, the EventEmitter is the observable and subject itself.
This is something working well with promises as long as we always return the same promise object. Since we have observers introduced with HTTP Angular 2 services, I would like to avoid mixing promises and observers (and observers are said to be more powerful than promises, so it should allow to do what is easy with promises).
Specs about share() (I haven't found doc for version 5 alpha - version used by Angular 2) - working on the Observable returned by the Angular 2 HTTP service, not working on EventEmitter.
EDIT: clarified why not using the Observable returned by HTTP and added that not using RxJS directly would be even better.
EDIT: changed description: the concern is about multiple subscriptions, not merging the HTTP results.
Thanks!
The functionality you seem to be describing is not that of a cold observable but more than of a Rx.BehaviourSubject. Have a look here for an explanation on Rxjs subjects : https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md.
I quote from there :
BehaviourSubject is similar to ReplaySubject, except that it only stored the last value it published. BehaviourSubject also requires a default value upon initialization. This value is sent to observers when no other value has been received by the subject yet. This means that all subscribers will receive a value instantly on subscribe, unless the Subject has already completed.
The Rx.AsyncSubject would be the closest in behaviour to a promise :
AsyncSubject is similar to the Replay and Behavior subjects, however it will only store the last value, and only publish it when the sequence is completed. You can use the AsyncSubject type for situations when the source observable is hot and might complete before any observer can subscribe to it. In this case, AsyncSubject can still provide the last value and publish it to any future subscribers.
Two more comments:
in your plunker : this._coldObservable = emitter.share();. Using share returns a hot observable!
EventEmitter actually extends subject in the first place
UPDATE :
Wrapping an EventEmitter around an Rx.Observable:
function toRx ( eventEmitter ) {
return Rx.Observable.create(function ( observer ) {
eventEmitter.subscribe(function listener ( value ) {observer.onNext(value)});
// Ideally you also manage error and completion, if that makes sense with Angular2
return function () {
/* manage end of subscription here */
};
};
)
}
Once you have that Rx.Observable, you can apply share(), shareReplay(1), anything you want.
My bet is that the Angular team will sooner or later propose a brigding function but if you don't want to wait, you can do it yourself.
ReplaySubject is doing what I was looking for. #robwormald provided a working example on gitter I slightly modified to better demonstrate.
Exposing HTTP response:
import {Injectable} from 'angular2/angular2';
import {Http} from 'angular2/http';
import {ReplaySubject} from '#reactivex/rxjs/dist/cjs/Rx'
#Injectable()
export class PeopleService {
constructor(http:Http) {
this.people = new ReplaySubject(1);
http.get('api/people.json')
.map(res => res.json())
.subscribe(this.people);
}
}
Subscribing multiple times:
// ... annotations
export class App {
constructor(peopleService:PeopleService) {
people.subscribe(v => {
console.log(v)
});
//some time later
setTimeout(() => {
people.subscribe(v => {
console.log(v)
});
people.subscribe(v => {
console.log(v)
});
},2000)
}
}
Full plunker
EDIT: the BehaviorSubject is an alternative. In this usecase, the difference is the initial value, for example if we want to display content from cache before updating with the HTTP response.

Categories