Ionic7集成AngularFire Auth出现NullInjectorError依赖缺失问题
Ionic7 + AngularFire 认证:
InjectionToken angularfire2.app.options 缺失问题 我正在用Ionic7和AngularFire实现Firebase认证,网上大部分教程都是旧版的app.module.ts配置方式,现在已经不适用了,所以我自定义了FirebaseModule:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { initializeApp, provideFirebaseApp } from '@angular/fire/app'; import { provideAuth, getAuth } from '@angular/fire/auth'; import { provideFirestore, getFirestore } from '@angular/fire/firestore'; // Your web app's Firebase configuration // For Firebase JS SDK v7.20.0 and later, measurementId is optional const firebaseConfig = { apiKey: "XXXXX", ... }; @NgModule({ declarations: [], imports: [ CommonModule, provideFirebaseApp( () => initializeApp(firebaseConfig)), provideAuth(() => getAuth()), provideFirestore(() => getFirestore(), ), ], providers: [] }) export class FirebaseModule { }
并编写了认证服务AuthenticationServiceService:
//reference https://www.positronx.io/ionic-firebase-authentication-tutorial-with-examples/ import { Injectable, NgZone } from '@angular/core'; import { AngularFirestore, AngularFirestoreDocument, } from '@angular/fire/compat/firestore'; import { AngularFireAuth } from '@angular/fire/compat/auth'; import { Router } from '@angular/router'; import { User } from './modal/user.modal'; import * as auth from 'firebase/auth'; @Injectable({ providedIn: 'root' }) export class AuthenticationServiceService { userData: any; constructor( public afStore: AngularFirestore, public ngFireAuth: AngularFireAuth, public router: Router, public ngZone: NgZone) { this.ngFireAuth.authState.subscribe((user) => { if (user) { this.userData = user; localStorage.setItem('user', JSON.stringify(this.userData)); JSON.parse(localStorage.getItem('user') || '{}'); } else { localStorage.setItem('user', null || '{}'); JSON.parse(localStorage.getItem('user') || '{}'); } }); } // Returns true when user is looged in get isLoggedIn(): boolean { const user = JSON.parse(localStorage.getItem('user') || '{}'); return user !== null && user.emailVerified !== false ? true : false; } // Returns true when user's email is verified get isEmailVerified(): boolean { const user = JSON.parse(localStorage.getItem('user') || '{}'); return user.emailVerified !== false ? true : false; } // Sign in with Gmail GoogleAuth() { return this.AuthLogin(new auth.GoogleAuthProvider()); } // Auth providers AuthLogin(provider: any) { return this.ngFireAuth .signInWithPopup(provider) .then((result) => { this.ngZone.run(() => { this.router.navigate(['dashboard']); }); this.SetUserData(result.user); }) .catch((error) => { window.alert(error); }); } // Store user in localStorage SetUserData(user: any) { const userRef: AngularFirestoreDocument<any> = this.afStore.doc( `users/${user.uid}` ); const userData: User = { uid: user.uid, email: user.email, displayName: user.displayName, photoURL: user.photoURL, emailVerified: user.emailVerified, }; return userRef.set(userData, { merge: true, }); } // Sign-out SignOut() { return this.ngFireAuth.signOut().then(() => { localStorage.removeItem('user'); this.router.navigate(['login']); }); } }
随后在独立组件Tab1Page中引入FirebaseModule并注入该服务:
import { FirebaseModule } from '../firebase/firebase.module'; import { AuthenticationServiceService } from '../authentication-service.service'; @Component({ selector: 'app-tab1', templateUrl: 'tab1.page.html', styleUrls: ['tab1.page.scss'], standalone: true, imports: [IonicModule, CommonModule, FirebaseModule, FormsModule, ReactiveFormsModule], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class Tab1Page { constructor(public authService: AuthenticationServiceService) {} }
但运行时出现如下错误:
ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(Standalone[Tab1Page])[AuthenticationServiceService -> AuthenticationServiceService -> AuthenticationServiceService -> AuthenticationServiceService -> AngularFirestore -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]: NullInjectorError: No provider for InjectionToken angularfire2.app.options! NullInjectorError: R3InjectorError(Standalone[Tab1Page])[AuthenticationServiceService -> AuthenticationServiceService -> AuthenticationServiceService -> AuthenticationServiceService -> AngularFirestore -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]: NullInjectorError: No provider for InjectionToken angularfire2.app.options! at NullInjector.get (core.mjs:7493:27) at R3Injector.get (core.mjs:7914:33) at R3Injector.get (core.mjs:7914:33) at injectInjectorOnly (core.mjs:618:33) at Module.ɵɵinject (core.mjs:622:60) at Object.AngularFirestore_Factory [as factory] (angular-fire-compat-firestore.js:584:102) at R3Injector.hydrate (core.mjs:8015:35) at R3Injector.get (core.mjs:7903:33) at injectInjectorOnly (core.mjs:618:33) at Module.ɵɵinject (core.mjs:622:60) at resolvePromise (zone.js:1211:31) at resolvePromise (zone.js:1165:17) at zone.js:1278:17 at _ZoneDelegate.invokeTask (zone.js:406:31) at core.mjs:23896:55 at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:23896:36) at _ZoneDelegate.invokeTask (zone.js:405:60) at Object.onInvokeTask (core.mjs:24197:33) at _ZoneDelegate.invokeTask (zone.js:405:60) at Zone.runTask (zone.js:178:47)
问题原因
你混用了AngularFire的模块化API(provideFirebaseApp等)和兼容层API(AngularFireAuth、AngularFirestore),导致依赖注入不匹配。兼容层需要传统的AngularFireModule.initializeApp()配置,而不是新的provideFirebaseApp。
修复方案
方案1:调整FirebaseModule支持兼容层
修改FirebaseModule,添加兼容层模块的初始化,这是让AngularFireAuth和AngularFirestore正常工作的关键:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; // 导入兼容层模块 import { AngularFireModule } from '@angular/fire/compat'; import { AngularFireAuthModule } from '@angular/fire/compat/auth'; import { AngularFirestoreModule } from '@angular/fire/compat/firestore'; // 保留新的模块化API(可选) import { initializeApp, provideFirebaseApp } from '@angular/fire/app'; import { provideAuth, getAuth } from '@angular/fire/auth'; import { provideFirestore, getFirestore } from '@angular/fire/firestore'; const firebaseConfig = { apiKey: "XXXXX", // 你的其他配置项 }; @NgModule({ declarations: [], imports: [ CommonModule, // 兼容层初始化,必须添加 AngularFireModule.initializeApp(firebaseConfig), AngularFireAuthModule, AngularFirestoreModule, // 新的模块化API可以保留,两者不冲突 provideFirebaseApp(() => initializeApp(firebaseConfig)), provideAuth(() => getAuth()), provideFirestore(() => getFirestore()) ], providers: [] }) export class FirebaseModule { }
方案2:迁移到纯模块化API(推荐)
兼容层会逐渐被官方淘汰,建议直接使用AngularFire的新模块化API,修改认证服务如下:
import { Injectable, NgZone } from '@angular/core'; import { Auth, signInWithPopup, GoogleAuthProvider, signOut, authState } from '@angular/fire/auth'; import { Firestore, doc, setDoc } from '@angular/fire/firestore'; import { Router } from '@angular/router'; import { User } from './modal/user.modal'; @Injectable({ providedIn: 'root' }) export class AuthenticationServiceService { userData: any; constructor( private auth: Auth, private firestore: Firestore, private router: Router, private ngZone: NgZone ) { authState(this.auth).subscribe((user) => { if (user) { this.userData = user; localStorage.setItem('user', JSON.stringify(this.userData)); JSON.parse(localStorage.getItem('user') || '{}'); } else { localStorage.setItem('user', '{}'); JSON.parse(localStorage.getItem('user') || '{}'); } }); } get isLoggedIn(): boolean { const user = JSON.parse(localStorage.getItem('user') || '{}'); return user !== null && user.emailVerified !== false; } get isEmailVerified(): boolean { const user = JSON.parse(localStorage.getItem('user') || '{}'); return user.emailVerified !== false; } GoogleAuth() { return this.AuthLogin(new GoogleAuthProvider()); } AuthLogin(provider: any) { return signInWithPopup(this.auth, provider) .then((result) => { this.ngZone.run(() => { this.router.navigate(['dashboard']); }); this.SetUserData(result.user); }) .catch((error) => { window.alert(error); }); } async SetUserData(user: any) { const userRef = doc(this.firestore, `users/${user.uid}`); const userData: User = { uid: user.uid, email: user.email, displayName: user.displayName, photoURL: user.photoURL, emailVerified: user.emailVerified, }; await setDoc(userRef, userData, { merge: true }); } SignOut() { return signOut(this.auth).then(() => { localStorage.removeItem('user'); this.router.navigate(['login']); }); } }
这种情况下,你的原始FirebaseModule配置可以保持不变,不需要导入任何兼容层模块。
方案3:确认独立组件配置
无论采用哪种方案,Tab1Page的imports数组中保留FirebaseModule即可,无需额外修改。
内容的提问来源于stack exchange,提问作者Moblize IT
相关产品推荐
相关产品推荐

