Search code examples
javascriptangularfirebase-authenticationangularfire

How to solve Angular Firebase v.7 issue: Cannot use 'in' operator to search for '_delegate' in users/xxxxx


I am using the new Angular Firebase v.7 with Angular and I am getting an error: Cannot use 'in' operator to search for '_delegate' in users/1QAvZYg6aqe0GhA13tmVAINa.

There is a similar question ( Firebase Error: TypeError: Cannot use 'in' operator to search for '_delegate' in undefined ) but it is unanswered and I have been trying to little avail to find one myself. Perhaps I am overlooking something very simple.

The code

The service responsible for creating the document is in data.service.ts as looks like this:

import { Injectable } from '@angular/core';
import {
    collection, deleteDoc, doc, DocumentSnapshot, Firestore, onSnapshot,
    query, QuerySnapshot, setDoc, Timestamp as fTimestamp, Unsubscribe
} from '@angular/fire/firestore';
import { firstValueFrom, lastValueFrom, Observable, Subject } from 'rxjs';
import { take } from 'rxjs/operators';

export interface FirestoreExtDoc<T> {
    data: Observable<T>;
    unsubscribe: Unsubscribe;
}
export interface FirestoreExtCol<T> {
    data: Observable<T[]>;
    unsubscribe: Unsubscribe;
}

@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(private db: Firestore) { }
    upsert<DocumentData>(ref: string, data: any): Promise<void> {
        const docRef = doc(this.db, ref);
        const timestamp = fTimestamp.now();
        const newData = {
            ...data,
            updatedAt: timestamp,
            createdAt: timestamp,
        };
        const updatedData = {
            ...data,
            updatedAt: timestamp,
        };
        const snapshot = lastValueFrom(this.getDoc<DocumentData>(ref).data.pipe(take(1)));
        return snapshot.then(
            snap => (snap as any).exists ?
                setDoc(docRef, updatedData, { merge: true }) :
                setDoc(docRef, newData, { merge: true })
            );
    };
}

In my auth.service.ts I import the above data service and call the upsert method like this:

import { Injectable } from '@angular/core';
import {
  Auth,
  signInWithEmailAndPassword,
  GoogleAuthProvider,
  authState,
  createUserWithEmailAndPassword,
  updateProfile,
  UserInfo,
  signInWithPopup,
  sendPasswordResetEmail
} from '@angular/fire/auth';
import { 
  doc,
  collection,
  collectionGroup,
  setDoc,
  updateDoc,
  deleteDoc,
  docSnapshots,
  docData,
  getDoc
} from '@angular/fire/firestore';
import { User } from '@core/interfaces/user';


import { concatMap, from, Observable, of, switchMap } from 'rxjs';
import { DataService } from './data.service';

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

  currentUser$ = authState(this.auth);

  constructor(
    private auth: Auth,
    private dataService: DataService
    ) {}

  signUp(name: string, email: string, password: string): Observable<any> {
    return from(
      createUserWithEmailAndPassword(this.auth, email, password)
    ).pipe(switchMap(({ user }) => updateProfile(user, { displayName: name }).then((data) => { this.dataService.upsert(`users/${user.uid}`, data) }) ));
  }
}

Solution

  • In case you decide to go with a Cloud Function, the below code I use across several different apps and projects and it works faultlessly every time a user either signs up or is created by an 'Admin' user...

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    const FieldValue = require('firebase-admin').firestore.FieldValue;
    
    admin.initializeApp();
    
    const db = admin.firestore();
    
    
    
    /**
     * Add user to firestore
     */
    const creatProfile = (userRecord) => {
        const uid = userRecord.uid;
        const admin = false;
        const email = userRecord.email;
        const photoURL = userRecord.photoUrl || 'enter shortened url for default image';
        const name = userRecord.displayName || 'New User';
        const spouse = userRecord.spouse || 'TBA';
        const forumUserName = userRecord.forumUserName || 'New Username set by admin';
        const address = userRecord.address || 'TBA';
        const suburb = userRecord.suburb || 'TBA';
        const state = userRecord.state || 'QLD';
        const postCode = userRecord.postCode || '2000';
        const homePhone = userRecord.homePhone || '02 1234 5678';
        const mobilePhone = userRecord.mobilePhone || '0400 123 456';
        const memNum = userRecord.memNum || 123;
        const timestamp = FieldValue.serverTimestamp();
        const memType = userRecord.memType || 'Nominated';
        const memStatus = userRecord.memStatus || `Pending`;
        const isStateCoord = userRecord.isStateCoord || false;
        const stateCoordState = userRecord.stateCoordState || 'QLD';
        //const newUserRef = db.doc(`users/${uid}`)
        
        // Convert any date to timestamp for consistency
    
        
    
        return db
            .collection(`users`)
            .doc(userRecord.uid)
            .set({
            uid: uid,
            email: email,
            photoURL: photoURL,
            fullName: name,
            mDOB: timestamp,
            spouse: spouse,
            sDOB: timestamp,
            forumUserName: forumUserName,
            address: address,
            suburb: suburb,
            state: state,
            postCode: postCode,
            homePhone: homePhone,
            mobilePhone: mobilePhone,
            memNum: memNum,
            memType: memType,
            memStatus: memStatus,
            memDueDate: timestamp,
            lastLoginDate: timestamp,
            joined: timestamp,
            updated: timestamp,
            admin: admin,
            isAdmin: isAdmin,
            isStateCoord: isStateCoord,
            stateCoordState: stateCoordState,
            
        })
        
            .catch(console.error);
    };
    
    
    exports.authOnCreate = functions.auth.user().onCreate(creatProfile);
    

    All of the info in const createProfile are variables and can be what ever you need them to be.