DEV Community

Cover image for Angular 12 with Firebase 9
Jonathan Gamble
Jonathan Gamble

Posted on • Updated on

Angular 12 with Firebase 9

Update 3/7/24

For a modern Angular Firebase App, see my latest post:
https://code.build/p/angular-todo-app-with-firebase-fmLJ37


Original Post


Quick Reference

Angular's Docs have not been updated fully yet, so I made a quick reference.

app.module.ts - Imports

import { provideFirebaseApp, initializeApp } 
from '@angular/fire/app';
import { getAuth, provideAuth } 
from '@angular/fire/auth';
import { getFirestore, provideFirestore } 
from '@angular/fire/firestore';
import { getStorage, provideStorage } 
from '@angular/fire/storage';
import {
  getAnalytics,
  provideAnalytics,
  ScreenTrackingService,
  UserTrackingService
} from '@angular/fire/analytics';
...

@NgModule({
  declarations: [],
  imports: [
    provideAnalytics(() => getAnalytics()),
    provideFirebaseApp(() => initializeApp(environment.firebase)),
    provideFirestore(() => getFirestore()),
    provideAuth(() => getAuth()),
    provideStorage(() => getStorage())
    ...
  ],
  providers: [
    ScreenTrackingService,
    UserTrackingService
  ],
})
Enter fullscreen mode Exit fullscreen mode

Note: For Angular Universal SSR, you may have problems with provideAnalytics(). Either use the old version, or only load it on the server version. I could not get it to work correctly in regular Angular, but they may have fixed the code in a newer version.

import

import {
  collection,
  doc,
  docData,
  DocumentReference,
  CollectionReference,
  Firestore,
  onSnapshot,
  query,
  where,
  Unsubscribe,
  Query,
  DocumentData,
  collectionData,
  collectionChanges,
  docSnapshots,
  ...
} from '@angular/fire/firestore';
Enter fullscreen mode Exit fullscreen mode

constructor

constructor(
  private afs: Firestore
) { }
Enter fullscreen mode Exit fullscreen mode

Documents

valueChanges()

docData<Post>(
  doc(this.afs, 'posts', id)
);
Enter fullscreen mode Exit fullscreen mode

snapShotChanges()

docSnapshots<Post>(
  doc(this.afs, `posts/${id}`)
);
Enter fullscreen mode Exit fullscreen mode

Collections

valueChanges()

collectionData<Post>(
  query<Post>(
    collection(this.afs, 'posts') as CollectionReference<Post>,
    where('published', '==', true)
  ), { idField: 'id' }
);
Enter fullscreen mode Exit fullscreen mode

snapShotChanges()

collectionChanges<Post>(
  query<Post>(
    collection(this.afs, 'posts') as CollectionReference<Post>,
    where('published', '==', true)
  )
);
Enter fullscreen mode Exit fullscreen mode

createId()

doc(collection(this.afs, 'id')).id;
Enter fullscreen mode Exit fullscreen mode

Auth

imports

import {
  Auth,
  signOut,
  signInWithPopup,
  user,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  updateProfile,
  sendEmailVerification,
  sendPasswordResetEmail,
  getAdditionalUserInfo,
  OAuthProvider,
  linkWithPopup,
  unlink,
  updateEmail,
  updatePassword,
  User,
  reauthenticateWithPopup,
  authState,
  onAuthStateChanged
  ...
} from '@angular/fire/auth';
Enter fullscreen mode Exit fullscreen mode

Code

user$: Observable<User | null>;

constructor(private auth: Auth) {

  // user observable, not user doc
  this.user$ = user(auth);

  // or use this version...
  this.user$ = authState(auth);

  // or use this version...
  this.user$ = new Observable((observer: any) =>
    onAuthStateChanged(auth, observer)
  );

  // or pipe user doc, Profile interface
  // returns user doc, not User type
  // here this.user$: Observable<Profile | null>;
  this.user$ = user(auth).pipe(
      switchMap((user: User | null) =>
        user
          ? docData(doc(this.afs, 'users', user.uid))
            as Observable<Profile>
          : of(null)
      )
    );

}

async getUser(): Promise<User | null> {
  return await this.user$.pipe(take(1)).toPromise();
}

...

async emailLogin(email: string, password: string)
: Promise<any> {
  return await signInWithEmailAndPassword(this.auth, email, password);
}

async emailSignUp(email: string, password: string)
: Promise<void> {

  const credential = await createUserWithEmailAndPassword(
    this.auth,
    email,
    password
  );
  await updateProfile(
    credential.user, { displayName: credential.user.displayName }
  );
  await sendEmailVerification(credential.user);

  // create user in db
  ...
}

async resetPassword(email: string): Promise<any> {

  // sends reset password email
  await sendPasswordResetEmail(this.auth, email);
  ...
}

async oAuthLogin(p: string): Promise<void> {

  // get provider, sign in
  const provider = new OAuthProvider(p);
  const credential = await signInWithPopup(this.auth, provider);
  const additionalInfo = getAdditionalUserInfo(credential);

  // create user in db
  if (additionalInfo?.isNewUser) {
    ...
  }
}
Enter fullscreen mode Exit fullscreen mode

Storage

Import

import {
  Storage,
  ref,
  deleteObject,
  uploadBytes,
  uploadString,
  uploadBytesResumable,
  percentage,
  getDownloadURL,
  ...
} from '@angular/fire/storage';
Enter fullscreen mode Exit fullscreen mode

Code

uploadPercent: Observable<number>;

constructor(private storage: Storage) { }

async upload(
  folder: string,
  name: string,
  file: File | null
): Promise<string> {

  const ext = file!.name.split('.').pop();
  const path = `${folder}/${name}.${ext}`; {

  if (file) {
    try {
      const storageRef = ref(this.storage, path);
      const task = uploadBytesResumable(storageRef, file);
      this.uploadPercent = percentage(task);
      await task;
      const url = await getDownloadURL(storageRef);
    } catch(e: any) {
      console.error(e);
    }   
  } else {
    // handle invalid file
  }
  return url;
}
Enter fullscreen mode Exit fullscreen mode

I may update this with more items, but I wanted to be particular to Angular and not get into the general Firebase 9 updates. I didn't want to show every possible example, but you start to see the patterns.

J

Top comments (18)

Collapse
 
joel2k0 profile image
Joel • Edited

Type casting is not working here:

docSnapshots<Post>(
  doc(this.afs, `posts/${id}`)
);
Enter fullscreen mode Exit fullscreen mode
Argument of type 'DocumentReference<DocumentData>' is not assignable to parameter of type 'DocumentReference<Post>'.
  The types returned by 'converter.fromFirestore(...)' are incompatible between these types.
    Type 'DocumentData' is not assignable to type 'Post'.ts(2345)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jdgamble555 profile image
Jonathan Gamble • Edited

Yes, Angular Firebase is definitely not perfected code with Firebase 9. You can get around that with:

docSnapshots<Post>(
  doc(this.afs, `posts/${id}`) as DocumentReference<Post>
);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
free_the_bees profile image
Michael Peake

Thank you. This has been very useful today. I'm starting a new project and want to use the new APIs but the lack of documentation is infuriating.

Collapse
 
apl profile image
Ana Paula Lopes • Edited

Help... :(

   provideFirebaseApp(() => initializeApp(firebaseConfig)),
    provideFirestore(() => getFirestore()),
    provideStorage(() => getStorage()),
Enter fullscreen mode Exit fullscreen mode
bim.component.html:85 ERROR FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call initializeApp() first (app/no-app).
    at getApp (index.esm2017.js:479:29)
    at getStorage (index.esm2017.js:3558:33)
    at BimService.downloadAttachment (bim.service.ts:14:31)
    at BimComponent.getDownload_BT50 (bim.component.ts:21:18)
    at BimComponent_Template_a_click_56_listener (bim.component.html:85:36)
    at executeListenerWithErrorHandling (core.mjs:16795:16)
    at wrapListenerIn_markDirtyAndPreventDefault (core.mjs:16828:22)
    at HTMLAnchorElement.<anonymous> (platform-browser.mjs:665:17)
    at _ZoneDelegate.invokeTask (zone.js:402:31)
    at core.mjs:10757:55
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jdgamble555 profile image
Jonathan Gamble

Read my updated article on this - this article is out-of-date - code.build/p/angular-todo-app-with... - make sure you're using importProvidersFrom()

Collapse
 
vulps profile image
Andrew MacFarlane

I have been scouring the internet for 24 hours looking for an example of how to implement the new AngularFire Auth API!! thank you!!!

Collapse
 
alsmith141510 profile image
Alsmith141510

HI: This code doesn't work with 7.4.1 and angular 14.
this.user$ = user(auth).pipe(
switchMap((user: User | null) =>
user
? docData(doc(this.afs, 'users', user.uid))
as Observable
: of(null)
)
);

Collapse
 
paxforce profile image
Patrick Simonski

Thanks so much for this! You wrote "I may update this with more items..." and I cant' wait. We need articles like this, bc the official @angular/angularfire documentation is awful.

Collapse
 
jdgamble555 profile image
Jonathan Gamble

Is there something you specifically need? If so I can add it!

Collapse
 
paxforce profile image
Patrick Simonski

Nothing specific, just more of that juicy stuff - the more the better.

Collapse
 
neelavar profile image
Prasanna Neelavar

Very good reference and crisp representation of the required imports & implementation without any fluff. Thank you.

Collapse
 
kaliahaze profile image
Kalia Hayes

Great reference, thank you. Turns out when you step away from AngularFire for a year+ several things change lol.

Collapse
 
baluditor profile image
Baluditor

Thanks. I was loosing my sanity over the official docs.

Collapse
 
apluspluslabs profile image
A++Labs

great article
do u have anywhare u go in details about the use of collections search where, pagination... in angular 12+ firestore 9 (angularfire 7)

Collapse
 
jdgamble555 profile image
Jonathan Gamble

Yes, just look at the where examples above. Pagination will use limit() and orderBy() as usual, just import them.

Collapse
 
desoga profile image
deji adesoga

Exellent resource. I am currently working on a tutorial using Angular 12 with firebase 9.

Collapse
 
island_dev profile image
Joey The Dev

You saved a life today. Thank you

Collapse
 
dragosilies profile image
DragosIlies

When adding a new document to firestore, how do we tell it to auto generate id? If I don't put id in doc() it gives me an error.