Search code examples
angulartypescriptfirebasegoogle-cloud-firestorerxjs-observables

Angular firestore get document field as string


I know there is a ton of questions like this, but none of the solutions solve my problem. IVe been stuck on this for a while, and would really appreciate some help/guidance. I suppose there is something big I am missing in the ideas of observable, cloud firestore, etc...

Primary Issue: How can I get a single document field from cloud firestore db as a string? I attempted two different approaches and niether gave me the results i need:

getUsername(uid : string) {
    const user = this.afs.firestore.doc(`users/${uid}`)
    return user.get().then((doc) => {
        return (doc.exists ? doc.data().username : null);})

} //this one returns a promise and i need a string
getUsername(uid : string) {
    let answer;
    this.get(uid).valueChanges().subscribe((doc) => answer = doc.username) //intellisense reccomended username as m attribute which was hopeful
    return answer;
} //this returns undefined    
 

Quick aside- ive seen people use the rxjs map operator to access the fields in an object, like

getUsername() return userDoc.pipe(map(user => user.username))

I tried this one too, and it did not work. I also checked the rxjs documentation and it said the intended use of map was for applying an operation to observable stream. Can it be used like how shown above? ( and if so, what am i doing wrong? )

Details/background: I am using Google Firebase for both authentication and Firestore cloud db. The problem arises because users create a user account through my application (creating a new user document in the users collection), but on the backend, i use firebase createUserWithEmailAndPassword method- so there is two seperate instances of user data- google record and my application. To resolve this, I use the firebase.user.uid as the uid for the user document, and sync googles data with my application db during runtime, so that if a user changes their gmail address, their user document has the most up to date email address. The problem here is that I have a seperate collection "profiles" that stores the user public profile and preference data, where username is used as the uid, and also is used as query parameter in the url like

localhost/4200/profiles/username

so for here, the username has to be one word without spaces. I validate for this in the create-account form, but there is a separate issue now- google username can contain spaces. So when I sync the google account with my user document, i have to use two data sources.

userDoc.set({
username : from user document
email : from firebase.User.email
uid : Firebase.User.uid
})

and so, a few questions arise from this.

  1. how do i get a single firestore document field as a string rather than an promise, observable, [object object] etc..?
  2. should i go back to the drawing board with this whole situation- (

ie

  • redesign my db so that profile is a subcollection of user
  • find a way to navigate to profile page without using username as a route parameter
  • think of a better plan for authentication, so that there isnt a seperate db table that needs to get synced to googles auth data whenever the user logs in

Any help or suggestion(s) are greatly appreciated


Solution

  • I would try in my component ts

    ...
    username:string
    ...
    
    constructor(private db:FirestoreService)
    
    ...
    
    getUsername(){
      this.db.getUsername(someUID).subscribe(
        (data) => this.username = data.exists ? data.data().username : undefined
      }
    }
    

    and in FirestoreService ts

    getUsername(uid : string) {
        return (this.afs.firestore.doc(`users/${uid}`).get())
    }
    

    now to answer a few questions (that's normally only one problem at a time), you can find many tutorial to have both a firebase authentification and specific user infos on firestore or realtime database.

    You cannot return an asynchronous retrieved value in a syncronous flow you'll always have to update your app later with the desired value after being retrieved from the db.