I'm using AngularFire to manage user inside my app. I'm still learning about observables and rxjs.
I'm trying to call signInWithEmailAndPassword
method - which return a promise - and to retrieve a doc from a collection.
In my service I get the document using this method:
getDbUser(uid): Observable<User> { return this.angularFirestore.collection<User>(USERS).doc(uid).valueChanges() }
and I login user with:
login(user: User) { // sign in con email e password return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password).then((result) => { if(result.user?.uid) { // get user doc return this.getDbUser(result.user.uid); } return null; }); }
With this code, calling login method requires to solve a promise and then subscribe to the observable, like this:
this.userService.login(this.loginUser).then((userObs) => { userObs.subscribe((user) => { console.log(user); }); })
I would really like to avoid this but I'm not sure how to to do. I tried using RxJs toPromise
operator:
login(user: User) { // sign in con email e password return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password).then((result) => { if(result.user?.uid) { // recupero l'utente dal db return this.getDbUser(result.user.uid).toPromise(); } return null; }); }
but somehow this is not working (the promise is never resolved).
The reason toPromise() doesn't work because the observable stays open. Use either the first operator or if you're using a later version of rxjs, firstValueFrom . Read more about this at Conversion to Promises from the rxjs documentation.
Since you're using valuesChanges instead of get , then maybe your intention is to keep the observable open. If that's the case then the best thing to do is to convert the login to an observable using from .
login(user: User) {
return from(this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password)).pipe(
switchMap(res => (res.user.uid)
? this.getDbuser(res.user.uid)
: of(null))
);
}
Try using rxjs operators like mergeMap, swtichMap, pipe, map etc.You can avoid nested subscription with those.
if you want to let know of any obserable consumer that a user just logged in only after signInWithEmailAndPassword() has returend i would decalre a subject and subsscribe to user$ from any service consumers.
private userSubject$:Subject<string>=new Subject<string>();
public user$:Observable<string>=this.userSubject$.asObservable();
login(user: User) {
// sign in con email e password
return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password)
.then((result) => {
if(result.user?.uid) {
// recupero l'utente dal db
return this.userSubject$.next(result.user.uid);
}
return null;
});
} and from outside you will use the service:
this.userService.login(this.loginUser);
this.userService.user$.subscribe(user=>console.log);
The first comment is the correct answer:
You can add
.pipe(first())
before.toPromise
"
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.