Skip to content

Latest commit

 

History

History
298 lines (250 loc) · 8.29 KB

version-7-upgrade.md

File metadata and controls

298 lines (250 loc) · 8.29 KB

Upgrading to AngularFire 7.0

Intended to be run with Angular 12, AngularFire 7.0 allows you to take full advantage of the new tree-shakable Firebase JS SDK (v9) while also providing a compatible experience with the prior API.

ng update @angular/fire

Breaking changes

  • Angular 12 is required
  • AngularFire now only works in Ivy applications
  • Firebase JS SDK v9 is required
  • The existing AngularFire v6 API surface has moved from @angular/fire/* to @angular/fire/compat/* (see compatibility mode)
  • compat/auth: USE_EMULATOR DI token is now in the form of ['http://localhost:9099']

Compatibility mode

AngularFire v7.0 has a compatibility layer that supports the AngularFire v6.0 API. Just change your imports from @angular/fire/* to @angular/fire/compat/* and firebase/* to firebase/compat/*.

While not as tree-shakable as the new modular SDK, this allows you to upgrade and take advantage of the benefits of the new SDK ASAP.

Most developers can stop here for now as the new API isn't feature complete.

NEW Modular SDK

Initialization

In order to better support the tree-shakability introduced in Firebase v9 & to reduce the maintence required when the JS SDK adds new configuration flags, AngularFire providers now take a factory for a fully instantiated instance of the SDK you'd like to inject.

Before:

@NgModule({
    imports: [
        AngularFireModule.initializeApp(config),
        AngularFirestoreModule.enablePersistence(),
        AngularFireStorageModule,
    ],
    providers: [
        { provide: USE_EMULATOR, useValue: ['localhost', 8080] },
    ],
})

Modular SDK:

@NgModule({
    imports: [
        provideFirebaseApp(() => initializeApp(config)),
        provideFirestore(() => {
            const firestore = getFirestore();
            connectFirestoreEmulator(firestore, 'localhost', 8080);
            enableIndexedDbPersistence(firestore);
            return firestore;
        }),
        provideStorage(() => getStorage()),
    ],
})

Injecting services

Before when you injected Firebase JS SDK services into AngularFire they would be lazy-loaded and a promise-proxy would be returned to you. In AngularFire v7 you get the intiated service directly. We no longer lazy load for you.

import { Firestore, doc, onSnapshot, DocumentReference, docSnapshots } from '@angular/fire/firestore';

@Component({})
export class Foo {
    doc: DocumentReference;
    constructor(
        firestore: Firestore, // Injects the instantiated Firestore instance
    ) {
        // You can directly operate on the instance with JS SDK methods which we've
        // reexported in AngularFire
        this.doc = doc(firestore, 'foo/1');
        onSnapshot(doc, snap => {
            // ...
        });
        // or use the convenience observables
        docSnapshots(doc).subscribe(...);
    }
    async update() {
        await updateDoc(this.doc, { ... });
        ...
    }
}

Working with multiple apps / instances

In AngularFire v7 working with multiple instances was difficult, in the new SDK we have new DI tokens that make working with them much more straight forward.

@NgModule({
    imports: [
        provideFirebaseApp(() => initializeApp(config)),
        provideFirebaseApp(() => initializeApp(config2, 'anotherApp')),
        provideStorage(() => getStorage()),
        provideStorage(() => getStorage(getApp(), 'anotherBucket')),
        provideStorage(() => getStorage(getApp('anotherApp'))),
    ],
})
import { FirebaseApp, FirebaseApps } from '@angular/fire/app';
import { Storage, StorageInstances } from '@angular/fire/storage';

export class Foo {
    constructor(
        defaultApp: FirebaseApp,       // Injects the default FirebaseApp
        allFirebaseApps: FirebaseApps, // Injects an array of all initialized Firebase Apps
        storage: Storage,                      // Injects the default storage instance
        allStorageInstances: StorageInstances, // Injects an array of all the intialized storage instances
    ) { }
}

How the main injection tokens (i.e, FirebaseApp, Storage) function have changed from v7 but it should provide a much more powerful and intuitive API.

API

Beyond Depdency Injection AngularFire is sporting an entirely new API:

  1. We no longer handle lazy-loading the Firebase JS SDK modules for you
  2. We no longer provide classes beyond Depedency Injection
  3. No more Proxy / Promise-Proxy
  4. We reexport and Zone-wrap all Firebase and RxFire APIs

So developing with the new AngularFire is easy, you can use it just like the vanilla Firebase JS SDK. Just change all your firebase/app imports to @angular/fire/app, firebase/firestore to @angular/fire/firestore, firebase/database to @angular/fire/database, etc. Then if you're feeling comfortable with RXJS and would like to use some of our convenience operators you can just dip into that toolbox.

Alternatives to v6 APIs

v6 / Compat v7 Modular
AngularFirestore doc
import { doc } from '@angular/fire/firestore';
doc<T>(firestore, 'foo/bar') // DocumentReference<T>
collection
import { collection } from '@angular/fire/firestore';
collection<T>(firestore, 'foo') // CollectionReference<T>
collectionGroup
import { collectionGroup } from '@angular/fire/firestore';
collectionGroup<T>(firestore, 'foo') // Query<T>
AngularFirestoreDocument set
import { setDoc } from '@angular/fire/firestore';
setDoc(docRef, { ... }) // Promise<void>
update
import { updateDoc } from '@angular/fire/firestore';
updateDoc(docRef, { ... }) // Promise<void>
delete
import { deleteDoc } from '@angular/fire/firestore';
deleteDoc(docRef) // Promise<void>
collection
import { collection } from '@angular/fire/firestore';
collection<T>(docRef, 'bar') // CollectionReference<T>
snapshotChanges
import { docSnapshots } from '@angular/fire/firestore';
docSnapshots<T>(docRef) // Observable<DocumentSnapshot<T>>
valueChanges
import { docData } from '@angular/fire/firestore';
docData<T>(docRef) // Observable<T>
get
import { getDoc } from '@angular/fire/firestore';
getDoc<T>(docRef) // Promise<DocumentSnapshot<T>>

Code splitting and lazy-loading

AngularFire does not lazy-load services any longer. We have provided a helper observable for detecting when a new service instance is instantiated. In this example we'll code split out of all the Firestore related code and lazy-load

// firestore_operations.ts
import {
    collectionData,
    firestoreInstance$,
    collection,
    getFirestore
} from '@angular/fire/firestore';
import { first } from 'rxjs/operators';
import { IFoo } from '../interfaces';

export { getFirestore };

export const fooData = firestoreInstance$.pipe(
    first(),
    concatMap(firestore => collectionData<IFoo>(collection(firestore, 'foo'))),
);
export class AuthService {
    constructor() {
        getRedirectResult().then(result => {
            // Initialize Firestore only after a user logs in
            if (result.user) {
                const { getFirestore } = await import('./firestore_operations');
                getFirestore();
            }
        });
    }
}
@Component({})
export class Foo {
    data: Observable<IFoo[]>;
    constructor() {
        this.data = of(undefined).pipe(
            concatMap(() => import('./firestore_operations')),
            concatMap(it => it.fooData)
        );
    }
}