Description
I think the function argument that provideFirebaseApp()
accepts should have the following signature fn: (injector: Injector) => IFirebaseApp
instead of fn: () => IFirebaseApp
Additional Context
The function argument that provideFirebaseApp()
accepts, is expected to not take any arguments [1] but in practice when it is invoked it is passed an injector [2].
Passing an injector makes sense since our firebase initialization logic that needs to run inside that function may have some dependencies (like options in our usecase explained below). I suggest the signature for provideFirebaseApp()
to change into
function provideFirebaseApp(fn: (injector?: Injector) => IFirebaseApp, ...deps: any[]): ModuleWithProviders<FirebaseAppModule>
It seems that the original authors were aware of this usecase because:
- The actually passes the injector instance when invoking the provided function.
provideFirebaseApp
actually accepts a list of dependenciesdeps
and adds them as the module dependencies.
[1]
angularfire/src/app/app.module.ts
Line 71 in d724d81
[2]
angularfire/src/app/app.module.ts
Line 48 in d724d81
Version info
Angular: 12.0.3
Firebase: 9.1.0
AngularFire: 7.1.1
Other (e.g. Ionic/Cordova, Node, browser, operating system):
How to reproduce these conditions
Here is a sample snippet code where I am trying to pass in firebase options using a DI token. This is very useful if my options are fetched dynamically from a server.
export const MY_FIREBASE_OPTIONS_TOKEN = new InjectionToken<FirebaseOptions>(
'my-firebase-options'
);
@NgModule({
imports: [
CommonModule,
// ******* THIS IS WHERE THE ISSUE LIES ******
provideFirebaseApp((injector: any) => {
const firebaseOptions = injector.get<FirebaseOptions>(
MY_FIREBASE_OPTIONS_TOKEN
);
return initializeApp(firebaseOptions);
}, MY_FIREBASE_OPTIONS_TOKEN),
// Use 'session' auth state persistence which means the firebase session
// only persist in the current session or tab, and will be cleared when the
// tab or window in which the user authenticated is closed. More info:
// https://firebase.google.com/docs/auth/web/auth-state-persistence
provideAuth(() =>
initializeAuth(getApp(), {
persistence: browserSessionPersistence,
})
),
],
declarations: [],
})
export class FirebaseTestModule {}
elsewhere in my AppModule I can do:
@NgModule({
imports: [FirebaseTestModule],
providers: [
{
provide: MY_FIREBASE_OPTIONS_TOKEN,
// You can even use an APP_INITIALIZER provider to fetch this config from server.
useValue: {
apiKey: 'my_key',
authDomain: 'my_domain',
},
},
],
})
export class AppModule {}
However this would produce the following typescript error:
error TS2345: Argument of type '(injector: Injector) => FirebaseApp' is not assignable to parameter of type '() => FirebaseApp'.
Steps to set up and reproduce
See: https://stackblitz.com/edit/angular-ivy-psv1mw?file=src%2Fapp%2Ffirebase-test%2Ffirebase-test.module.ts
Sample data and security rules
N/A
Debug output
N/A
Expected behavior
I should be able to use injector to access my dependencies.
Actual behavior
No clean way to access injector instance.
Workaround
I have found a workaround to satisfy both typescript while also gaining access to the injector.
// Workaround: use a rest spread argument to both appease typescript by
// matching the function signature while also giving us access to the passed
// in injector argument.
provideFirebaseApp(...args: any) => {
const injector = args[0] as Injector;
const firebaseOptions = injector.get<FirebaseOptions>(
MY_FIREBASE_OPTIONS_TOKEN
);
return initializeApp(firebaseOptions);
}),