DEV Community

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

Posted on • Edited 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 (19)

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
 
frdric_lemaitre_a66824f profile image
Frédéric Lemaitre

hello, I'm facing an error with firebase auth / angular. I well configure my Angular app module with provideAuth(() => getAuth()) under providers sections but even when including no code related to Auth inside my app, I get SyntaxError: Unexpected number in JSON at position 2 (line 1 column 3) related with file _/scs/abc-static/_/js/k=gapi.lb.fr.MtA0XocprA0.O/m=gapi_iframes/rt=j/sv=1/d=1/ed=1/am=AAAg/rs=AHpOoo90Qw_OxY6asHlYoeK8rr6SbH-ghg

firebase did work with firestore provideFirestore(() => getFirestore()) but not with Auth.

I don't know at all what to do since I execute no code yet linked with auth inside my components...

Can someone help?

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
 
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
 
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
 
baluditor profile image
Baluditor

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

Collapse
 
neelavar profile image
Prasanna Neelavar

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

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.