I am trying to wrap my head around observables. I love the way observables solve development and readability issues. As I read, benefits are immense.
Observables on HTTP and collections seem to be straight forward. How can I convert something like this to observable pattern.
This is from my service component, to provide authentication. I'd prefer this to work like other HTTP services in Angular2 - with support for data, error and completion handlers.
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(function(firebaseUser) {
// do something to update your UI component
// pass user object to UI component
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
Any help here would be much appreciated. The only alternative solution I had was to create EventEmitter
s. But I guess that's a terrible way to do things in services section
This question is related to
angular
firebase
rxjs
firebase-authentication
You can also use a Subject and trigger its next() function from promise. See sample below:
Add code like below ( I used service )
class UserService {_x000D_
private createUserSubject: Subject < any > ;_x000D_
_x000D_
createUserWithEmailAndPassword() {_x000D_
if (this.createUserSubject) {_x000D_
return this.createUserSubject;_x000D_
} else {_x000D_
this.createUserSubject = new Subject < any > ();_x000D_
firebase.auth().createUserWithEmailAndPassword(email,_x000D_
password)_x000D_
.then(function(firebaseUser) {_x000D_
// do something to update your UI component_x000D_
// pass user object to UI component_x000D_
this.createUserSubject.next(firebaseUser);_x000D_
})_x000D_
.catch(function(error) {_x000D_
// Handle Errors here._x000D_
var errorCode = error.code;_x000D_
var errorMessage = error.message;_x000D_
this.createUserSubject.error(error);_x000D_
// ..._x000D_
});_x000D_
}_x000D_
_x000D_
}_x000D_
}
_x000D_
Create User From Component like below
class UserComponent {_x000D_
constructor(private userService: UserService) {_x000D_
this.userService.createUserWithEmailAndPassword().subscribe(user => console.log(user), error => console.log(error);_x000D_
}_x000D_
}
_x000D_
You can add a wrapper around promise functionality to return an Observable to observer.
import { of, Observable, defer } from 'rxjs';
import { map } from 'rxjs/operators';
function getTodos$(): Observable<any> {
return defer(()=>{
return fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => {
return json;
})
});
}
getTodos$().
subscribe(
(next)=>{
console.log('Data is:', next);
}
)
try this:
import 'rxjs/add/observable/fromPromise';
import { Observable } from "rxjs/Observable";
const subscription = Observable.fromPromise(
firebase.auth().createUserWithEmailAndPassword(email, password)
);
subscription.subscribe(firebaseUser => /* Do anything with data received */,
error => /* Handle error here */);
you can find complete reference to fromPromise operator here.
You may also use defer. The main difference is that the promise is not going to resolve or reject eagerly.
Use from
to directly convert a previously created Promise to an Observable.
import { from } from 'rxjs';
// getPromise() is called once, the promise is passed to the Observable
const observable$ = from(getPromise());
observable$
will be a hot Observable that effectively replays the Promises value to Subscribers.
It's a hot Observable because the producer (in this case the Promise) is created outside of the Observable. Multiple subscribers will share the same Promise. If the inner Promise has been resolved a new subscriber to the Observable will get its value immediately.
Use defer
with a Promise factory function as input to defer the creation and conversion of a Promise to an Observable.
import { defer } from 'rxjs';
// getPromise() is called every time someone subscribes to the observable$
const observable$ = defer(() => getPromise());
observable$
will be a cold Observable.
It's a cold Observable because the producer (the Promise) is created inside of the Observable. Each subscriber will create a new Promise by calling the given Promise factory function.
This allows you to create an observable$
without creating and thus executing a Promise right away and without sharing this Promise with multiple subscribers.
Each subscriber to observable$
effectively calls from(promiseFactory()).subscribe(subscriber)
. So each subscriber creates and converts its own new Promise to a new Observable and attaches itself to this new Observable.
Most RxJS operators that combine (e.g. merge
, concat
, forkJoin
, combineLatest
...) or transform observables (e.g. switchMap
, mergeMap
, concatMap
, catchError
...) accept promises directly. If you're using one of them anyway you don't have to use from
to wrap a promise first (but to create a cold observable you still might have to use defer
).
// Execute two promises simultaneously
forkJoin(getPromise(1), getPromise(2)).pipe(
switchMap(([v1, v2]) => v1.getPromise(v2)) // map to nested Promise
)
Check the documentation or implementation to see if the operator you're using accepts ObservableInput
or SubscribableOrPromise
.
type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>;
// Note the PromiseLike ----------------------------------------------------v
type SubscribableOrPromise<T> = Subscribable<T> | Subscribable<never> | PromiseLike<T> | InteropObservable<T>;
The difference between from
and defer
in an example: https://stackblitz.com/edit/rxjs-6rb7vf
const getPromise = val => new Promise(resolve => {
console.log('Promise created for', val);
setTimeout(() => resolve(`Promise Resolved: ${val}`), 5000);
});
// the execution of getPromise('FROM') starts here, when you create the promise inside from
const fromPromise$ = from(getPromise('FROM'));
const deferPromise$ = defer(() => getPromise('DEFER'));
fromPromise$.subscribe(console.log);
// the execution of getPromise('DEFER') starts here, when you subscribe to deferPromise$
deferPromise$.subscribe(console.log);
import { from } from 'rxjs';
from(firebase.auth().createUserWithEmailAndPassword(email, password))
.subscribe((user: any) => {
console.log('test');
});
Here is a shorter version using a combination of some of the answers above to convert your code from a promise to an observable.
Source: Stackoverflow.com