0
votes

I recently asked this Question, which had a lot of information in it but I dont think I was asking the right question. Google Firebase Angular Firestore switchMap - Firebase Error DocumentReference.set().

My understanding of AngularFireAuth is that you use it to create a User object with a bunch of values, most of which are immutable. If I would like to add a custom value, I need to do that via Google Firestore.

I have created the following firestore entry with this information in it: .

I've also created a User Model:

export interface User {
  uid: string;
  email: string;
  displayName?: string;
  role: string;
  thursdayCampaign: Boolean;
  menagerieCoast: Boolean;
}

My understanding is that the following code should go to the built-in AngularFireAuth User object to get the uid, and then I should use switchMap to redirect to my firebase store. The problem I'm getting is that when I sign in, this.updateUserData recieves the proper credentials, and then tries to update the AngularFireAuth User Object with the data constant that I create there. This is causing the function to throw an error, as userRef.set(data, { merge: true}) is attempting to merge role, thursdayCampaign, and menagerieCoast, into the AngularFireAuth User object, when those values dont exist on that object.

My question is why is my reference not redirecting. Do I need to call this.user$ somewhere?

The code in question is below. I hope I'm asking this in a better way than my previous question.

import { Injectable } from "@angular/core";
import { Router } from '@angular/router';

import { auth } from 'firebase/app';
import {
    AngularFirestore,
    AngularFirestoreDocument
} from '@angular/fire/firestore'

import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { User } from './user1.model';

import * as firebase from 'firebase';
import { AngularFireAuth } from '@angular/fire/auth';



@Injectable({
  providedIn: 'root'
})
export class AuthService {

  user$: Observable<User[]>;

  constructor(private afAuth: AngularFireAuth,
              private afs: AngularFirestore,
              private router: Router) {

        //This is how we're getting into the firestoreDB        
        this.user$ = this.afAuth.authState.pipe(
          switchMap(user => {
            if (user){
              return this.afs.doc<User>(`/users/${user.uid}`).valueChanges();
            } else {
                return of(null)
            }
          })
        )
              } //end constructor

  public updateUserData(user){
    //sets user data to firestore on login
    const userRef: AngularFirestoreDocument<User> = this.afs.doc(`users/${user.uid}`)
    console.log("userRef", userRef)
    const data: User = {
      uid: user.uid,
      email: user.email,
      displayName: user.displayName,
      role: user.role,
      thursdayCampaign: user.thursdayCampaign,
      menagerieCoast: user.menagerieCoast
    }
    console.log("data", data)
     userRef.set(data, { merge: true})
  }


  async emailSignin(value){
    //const provider = new auth.EmailAuthProvider();
    const credential = await this.afAuth.signInWithEmailAndPassword(value.email, value.password)
    return this.updateUserData(credential.user), 
    this.router.navigate(['/home'])
  }


  async googleSignin(){
    const provider = new auth.GoogleAuthProvider();
    const credential = await this.afAuth.signInWithPopup(provider);
    return this.updateUserData(credential.user)
  }

Error when calling emailSignin:

ERROR Error: Uncaught (in promise): FirebaseError: [code=invalid-argument]: Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field role) FirebaseError: Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field role)

1
Have you looked into the value of user.role since the last time you asked this question? The error message is still telling you that it's undefined.Doug Stevenson
user.role is undefined here because role does not exist in the AngularFire User Object. If, for example, I remove role:user.role from updateUserData, i get the same error thrown for thursdayCampaign. Because those are custom properties that dont exist on the AngularFire User Object. You can see from the screenshot I provided, they do exist and are defined in the firestore. Weirdly, if I set role: "foo" in my data constant here, it will update the firestore from "guest" to "foo". So I can't figure out how to make role equal to the role already in the firestore.S.Slusky
The error message is telling you that you simply can't pass undefined. What are you trying to do exactly? Since you can't pass undefined, what would you like to happen here?Doug Stevenson
Most simply, I would like to be able to read and write to that firestore database. In discussing this, I'm actually wondering why I need that updateUserData function attached to my sign in. It's there because it was here: fireship.io/lessons/angularfire-google-oauth/… But I think that when you sign in, you dont need to be able to update that data. Just read it from the correct place.S.Slusky

1 Answers

2
votes

If the updateUserData function is only being called at the actual authentication, you can simply remove the role, thursdayCampaign, menagerieCoast from the data payload.

This is because the { merge: true} makes sure that only data existing in the payload is changed and not the whole document, so your other pre-existing fields on the document would be left untouched and you wouldn't get that error anymore.

The updateUserData method is actually needed for new user's signin, since it reflects data from firebase auth into your firestore db, meaning, its where your unique uid is created on the db. If you need to update users at a later point on your app's execution I would suggest that you create a separate method for that.

NOTE: This is actually described in the video tutorial that you shared on the comments to your question. For reference to the community on similar issue here is the link.