angularfire
angularfire copied to clipboard
@angular/[email protected] throws Circular dependency in DI detected for Analytics. when using Analytics feature
Version info
Versions:
$ ng version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 17.0.6
Node: 20.1.0
Package Manager: npm 9.7.1
OS: linux x64
Angular: 17.0.6
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1700.6
@angular-devkit/build-angular 17.0.6
@angular-devkit/core 17.0.6
@angular-devkit/schematics 17.0.6
@angular/cdk 17.0.2
@angular/fire 17.0.0
@angular/google-maps 17.0.2
@angular/material 17.0.2
@schematics/angular 17.0.6
ng-packagr 17.0.0
rxjs 7.8.1
typescript 5.2.2
zone.js 0.14.2
How to reproduce these conditions
Failing test unit, Stackblitz demonstrating the problem
Steps to set up and reproduce
in app.config.ts, provide the analytics feature:
importProvidersFrom([
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAnalytics(() => getAnalytics()),
provideAuth(() => getAuth()),
provideFirestore(() => getFirestore()),
provideFunctions(() => {
const functions = getFunctions();
// connectFunctionsEmulator(functions, 'localhost', 5001);
return functions;
}),
providePerformance(() => getPerformance()),
provideStorage(() => getStorage()),
]),
Sample data and security rules
Debug output
** Errors in the JavaScript console **
debug.operator.ts:31 ERROR FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call initializeApp() first (app/no-app).
at getApp (index.esm2017.js:479:29)
at getAnalytics (index.esm2017.js:1059:35)
at angular-fire.mjs:202:48
at angular-fire.mjs:134:59
at _ZoneDelegate.invoke (zone.js:368:26)
at Zone.run (zone.js:129:43)
at NgZone.runOutsideAngular (core.mjs:14591:28)
at runOutsideAngular (angular-fire.mjs:134:35)
at angular-fire.mjs:202:21
at firebase.provider.ts:22:42
ERROR Error: NG0200: Circular dependency in DI detected for Analytics. Find more at https://angular.io/errors/NG0200
at throwCyclicDependencyError (core.mjs:292:11)
at R3Injector.hydrate (core.mjs:6162:13)
at R3Injector.get (core.mjs:6037:33)
at angular-fire-analytics.mjs:196:40
at _ZoneDelegate.invoke (zone.js:368:26)
at Object.onInvoke (core.mjs:14695:33)
at _ZoneDelegate.invoke (zone.js:367:52)
at Zone.run (zone.js:129:43)
at zone.js:1257:36
at _ZoneDelegate.invokeTask (zone.js:402:31)
If I comment out where I provide Analytics, then Performance runs into an issue:
main.ts:6 NullInjectorError: R3InjectorError(Standalone[AppComponent])[Performance -> Performance -> Performance]:
NullInjectorError: No provider for Performance!
at NullInjector.get (core.mjs:5605:27)
at R3Injector.get (core.mjs:6048:33)
at R3Injector.get (core.mjs:6048:33)
at R3Injector.get (core.mjs:6048:33)
at ChainedInjector.get (core.mjs:15400:36)
at lookupTokenUsingModuleInjector (core.mjs:4116:39)
at getOrCreateInjectable (core.mjs:4164:12)
at ɵɵdirectiveInject (core.mjs:11974:19)
at ɵɵinject (core.mjs:917:60)
at inject (core.mjs:1001:12)
** Output from firebase.database().enableLogging(true); **
** Screenshots **
Expected behavior
Actual behavior
I have the same issue with the latest version 17.0.0 for AngularFireAuth. I get a circular dependency issue:
NullInjectorError: R3InjectorError(Standalone[ExampleComponent])[FirebaseAuthService -> FirebaseAuthService -> FirebaseAuthService -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
at NullInjector.get (core.mjs:5606:27)
at R3Injector.get (core.mjs:6049:33)
at R3Injector.get (core.mjs:6049:33)
at injectInjectorOnly (core.mjs:911:40)
at Module.ɵɵinject (core.mjs:917:60)
at Object.AngularFireAuth_Factory [as factory] (angular-fire-compat-auth.mjs:147:96)
at core.mjs:6169:43
at runInInjectorProfilerContext (core.mjs:867:9)
at R3Injector.hydrate (core.mjs:6168:17)
at R3Injector.get (core.mjs:6038:33)
If it is helpful I am happy to share the code
Any idea how to fix this issue also using standalone components?
@Aball985 can you share more of your setup/code? I don't have issues with anything other than analytics and performance
@jakehockey10 sure thing here are some screenshots of how I am using angular fire this was my first time setting it up
This is what caused the errors to appear below
was trying to add guard protection for un-authed users
let me know if I am missing something you would also like to see
@Aball985 I'll take a look in a few hours. Thanks for sharing. I have guards working on my end so I'd like to see if I can help you out!
@Aball985 I'll take a look in a few hours. Thanks for sharing. I have guards working on my end so I'd like to see if I can help you out!
thanks! idk if you have your auth working as well but I am seeing this weird console error with the signInWithPopup method as well
@Aball985 I tried attaching the AuthGuard to a route and didn't get any errors in the console unfortunately :( But after taking a look at your screenshots again, I did notice that your firebaseProviders isn't being included in the providers array of your ApplicationConfig, so maybe that's it?
Here is how I've been guarding my routes in my application:
import { canActivate, redirectUnauthorizedTo } from '@angular/fire/auth-guard';
export const profilePageRoute = {
path: 'profile',
loadComponent: () => import('./profile-page.component'),
...canActivate(() => redirectUnauthorizedTo(['/auth/login'])),
title: 'Profile',
};
I don't use the signInWithPopup(new GoogleAuthProvider()), but you want to be careful mixing the compat library components (AngularFireAuth, for example). Instead, you want to import the function signInWithPopup from @angular/fire/auth and pass in your Auth instance that you've injected into the NavbarComponent. For example:
import {
Auth,
EmailAuthProvider,
createUserWithEmailAndPassword,
getIdToken,
reauthenticateWithCredential,
sendEmailVerification,
sendPasswordResetEmail,
signInWithEmailAndPassword,
signOut,
updatePassword,
updateProfile,
user,
} from '@angular/fire/auth';
...
@Injectable({
providedIn: 'root',
})
export class AuthService {
private readonly _auth = inject(Auth);
...
async login(email: string, password: string) {
const credential = await signInWithEmailAndPassword(
this._auth,
email,
password
);
return credential;
}
...
}
@jakehockey10 thanks for the reply
So I did add like you said and then no errors happened but AuthGuard just didnt do anything
then I tried your way I also found out there is a redirectUnauthorizedTo / redirectLoggedInTo
I'm not used to this spread syntax for redirection like this it looks kind of odd but it seems to work but for some reason when user is logged out / logged in the page doesn't update till refresh I am guessing I also need to add something into the component itself to listen for auth and redirect if true/false? would this logic also need to be added to every component? I thought that was the Guards job to handle navigation if Auth true/false unless there is better way it seems to only account for on init and not as user is logged in / logged out -> navigate
@Aball985 that is weird that AuthGuard by itself didn't work as expected. I'd have to look into it more. But the reason the spread syntax is being used here is that @angular/fire's canActivate function is providing multiple properties for the Route object. Take a look at: https://github.com/angular/angularfire/blob/8157744c53e378c379b0bdedd480332b997f741a/src/auth-guard/auth-guard.ts#L20.
I'm not sure I follow the last issue you mention. Are you saying that, for example, when you are logged out, but then you login, you are not automatically redirected away from the login page? If this is what you are saying, then yes, the auth guards are not going to handle redirecting away from the page for you. Guards only get executed when the user or the developer causes a route change. The route change "asks" the guard if "it's okay to navigate there." But if you want to go away from the login page after successful login, you'll want to handle that with your code that is signing the user in.
For example, my login-page.component uses my AuthService by calling the login method I shared above:
@Component({
selector: 'app-login-page',
standalone: true,
imports: [
RouterLink,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatCardModule,
MatButtonModule,
],
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.scss'],
})
export class LoginPageComponent {
readonly #fb = inject(FormBuilder);
readonly #router = inject(Router);
readonly #auth = inject(AuthService);
readonly #snacks = inject(MatSnackBar);
readonly loading = signal(false);
readonly form = this.#fb.nonNullable.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.minLength(8), Validators.required]],
});
protected async onSubmit() {
this.loading.set(true);
const { email, password } = this.form.getRawValue();
try {
const credential = await this.#auth.login(email, password);
this.#snacks.open(
`Welcome back, ${credential.user.displayName || credential.user.email}!`
);
this.#router.navigate(['/']); // <--- Navigating away from the login page when sign in is successful
} catch (error) {
if (error instanceof FirebaseError) {
switch (error.code) {
case 'auth/user-not-found':
case 'auth/wrong-password':
this.#snacks.open('Invalid email or password');
break;
default:
this.#snacks.open('An unknown error occurred');
break;
}
}
}
this.loading.set(false);
}
}
Also, if you haven't already, I'd take a look at this documentation here: https://github.com/angular/angularfire/blob/master/docs/auth.md
Hope that helps! Keep me posted!
Ah yes I figured it out I had to make my login component handle routerNavigation also after login logout its working as intended now when switching routes as expected thanks for showing me this!