1

i'm using angular with firebase authentication. when i use onAuthStateChanged method to know if the user is logged in or not it does not return the user or even with angularfireAuth.currentUser when i want to retrieve the user data it's giving me this error: for example i used afAuth.currentUser.uid to use it in my query functions

Uncaught (in promise): TypeError: Cannot read property 'uid' of null

this is the code i call in my authService's Constructor:

 firebase.auth().onAuthStateChanged(firebaseUser => {
          if (firebaseUser) {
            console.log(firebaseUser.uid)
            this.user.next(firebaseUser)
            return (firebaseUser.uid)
          } else {
            return (null)
          }
    })

I think i have to do it asynchronously but i don't know how? Any Ideas?

AT82
  • 71,416
  • 24
  • 140
  • 167
Shakar Bakr
  • 57
  • 1
  • 10

3 Answers3

2

Since you are using angularfire, I would suggest you listen to authState. I see that you are exposing an observable, subject of some kind, since you are calling next. But I would assign the value from authState to an observable, which you can subscribe to to get realtime changes, as this is triggered whenever changes happen to the authState.

Service:

import { User } from 'firebase';

// ...

user$: Observable<User | null>;

constructor(
    private afAuth: AngularFireAuth
) {
    this.user$ = this.afAuth.authState;
}

Then in your components you can subscribe to user$:

this.myService.user$.subscribe(....)

Here though you need to remember to unsubscribe when component is destroyed!!

You can also write a function in the service, that returns the user once, and you don't need to worry about unsubscribing.

In service same code as above, but add:

getUser() {
  return this.user$.pipe(first())
}

Now when in components you subscribe to this function, your user is just emitted once.

If you need to use this user in some other async function that depends on for example the uid, you can chain these requests using switchMap or mergeMap. So what you can do is...

this.myService.getUser().pipe(
  switchMap((user: User) => {
    return this.getCat(user.uid)
  })
// will console true or false
).subscribe(data => console.log(data))
AT82
  • 71,416
  • 24
  • 140
  • 167
  • Thank you so much, can i unsubscribe it right after the subscribe function? like this: this.myService.user$.subscribe(....).unsebscribe(); ? – Shakar Bakr Nov 20 '19 at 17:22
  • And how can i use getUser() in this function? this.afs.collection('users').doc().collection('categories').get() – Shakar Bakr Nov 20 '19 at 18:12
  • Iif you just want the user **once** then use the `getUser` function I provided. I also added an example to how to chain requests if your next request depends on the `user`. Please read the linked documentations to further understand how `switchMap` and `mergeMap` works. – AT82 Nov 20 '19 at 18:26
  • can you exactly give me the function of returning current user id? i'm so dumb and new to both angular and rxjs :( – Shakar Bakr Nov 20 '19 at 18:50
  • i just want to set variable equal to that and use it in other functions too but it gives me undefined when i instead of showing the data to console set the id(variable) to data.uid – Shakar Bakr Nov 20 '19 at 18:53
  • I'm sorry, really having trouble understanding your question. Where is it `undefined`? Outside this function? If that is the case, remember, this is asynchronous. – AT82 Nov 20 '19 at 18:58
  • id:string; this.user$.subscribe(data => this.id = data.uid) console.log(this.id) //undefined how can i do this in a correct way – Shakar Bakr Nov 20 '19 at 18:59
  • This is **asynchronous**, you cannot access it outside the callback (subscribe). Please read this: https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular It is very important to understand this, since you will face this daily :) – AT82 Nov 20 '19 at 19:00
  • Thank you i knew a bit of it and read all of it. i need to do my functions inside the subscribe method, i mean, if i need to return some data from database, then i have to write them inside the subscribe function? is that appropriate? – Shakar Bakr Nov 20 '19 at 19:11
  • I can't say, since I don't know what `return some data from database` means. If it means that you need the `id` for a subsequent request, then I suggest using `switchMap` or `mergeMap` to chain requests (like shown in answer), since it's not recommended to nest subscribes. – AT82 Nov 20 '19 at 19:43
  • i asked the question again can you check it? – Shakar Bakr Nov 20 '19 at 19:55
  • I updated my answer here. like I said, you need to chain the requests. In this case you can pass the `uid` to `getCat()` function. – AT82 Nov 20 '19 at 19:59
0

Are you wrapping that code in to a function and calling that function when the page loads? Maybe try wrapping it in a function and calling the loginStatus function when the page first loads

function loginStatus() { 
    firebase.auth().onAuthStateChanged(firebaseUser => {
      if (firebaseUser) {
        console.log(firebaseUser.uid)
      this.user.next(firebaseUser)
      return (firebaseUser.uid)
    } else {
      return (null)
    }
  })
}
Trae
  • 41
  • 5
0

If you are using angularFire you can import AngularFireAuth from @angular/fire/compat/auth and do the following in your component:

constructor(
  private afa: AngularFireAuth
) { }

ngOnInit(): void {
  this.monitorAuthState()
}

private monitorAuthState = async () => {
  this.afa.onAuthStateChanged( user => {
    if(user) { console.log(user) }
  })
}
Mike Poole
  • 1,958
  • 5
  • 29
  • 41