Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject() must be called from an injection context such as a constructor in AngularFirestoreDoc after upgrading to Angular v19 #3621

Open
djamn opened this issue Feb 12, 2025 · 8 comments

Comments

@djamn
Copy link

djamn commented Feb 12, 2025

I recently upgraded all dependencies to Angular v19 (from v18). I was previously on Angular v18 where everything worked fine. However, now, my whole console is spammed with the following error:

ERROR RuntimeError: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`. Find more at https://angular.dev/errors/NG0203
    at injectInjectorOnly (core.mjs:1104:11)
    at ɵɵinject (core.mjs:1114:40)
    at inject (core.mjs:1199:10)
    at <instance_members_initializer> (angular-fire-compat-firestore.mjs:93:14)
    at new AngularFirestoreDocument (angular-fire-compat-firestore.mjs:98:3)
    at _AngularFirestore.doc (angular-fire-compat-firestore.mjs:621:12)
    at auth.service.ts:38:14
    at switchMap.js:16:17
    at OperatorSubscriber2._this._next (OperatorSubscriber.js:14:9)
    at Subscriber2.next (Subscriber.js:32:12)

However, my auth service at line 38:14 only has the following code:
.doc<User>(`users/${user.uid}`)

The whole constructor looks like this (it is a angular service class)

  providedIn: 'root',
})
export class AuthService {
  isLoggedIn$: Observable<boolean>;
  /** User data of database */
  user$: Observable<User | null | undefined>;
  userRoles$: Observable<string[]>;
  userId: string | undefined = undefined;
  username: string | undefined = undefined;

  constructor(
    readonly firestore: AngularFirestore,
    readonly fireAuth: AngularFireAuth,
    readonly router: Router,
  ) {
    this.user$ = this.fireAuth.authState.pipe(
      switchMap((user) => {
        if (user) {
          this.userId = user.uid;
          return this.firestore
            .doc<User>(`users/${user.uid}`)
            .valueChanges()
            .pipe(
              map((userData) => {
                if (userData) {
                  this.username = userData.username; // Assign username
                }
                return userData;
              }),
            );
        } else {
          this.userId = undefined;
          this.username = undefined;
          return new Observable<User | null>((observer) => observer.next(null));
        }
      }),
    );

But I dont really know, why it does not work anymore in angular 19.

My package dependencies:

 "dependencies": {
    "@angular/animations": "^19.1.5",
    "@angular/cdk": "^19.1.3",
    "@angular/common": "^19.1.5",
    "@angular/compiler": "^19.1.5",
    "@angular/core": "^19.1.5",
    "@angular/fire": "^19.0.0",
    "@angular/forms": "^19.1.5",
    "@angular/material": "^19.1.3",
    "@angular/platform-browser": "^19.1.5",
    "@angular/platform-browser-dynamic": "^19.1.5",
    "@angular/router": "^19.1.5",
    "@fortawesome/angular-fontawesome": "^1.0.0",
    "@fortawesome/free-brands-svg-icons": "^6.7.1",
    "@fortawesome/free-regular-svg-icons": "^6.7.1",
    "@fortawesome/free-solid-svg-icons": "^6.7.1",
    "@ng-select/ng-select": "^14.2.2",
    "@ngx-translate/core": "^15.0.0",
    "@ngx-translate/http-loader": "^8.0.0",
    "angular-build-info": "^2.0.1",
    "canvas-confetti": "^1.9.3",
    "flowbite": "^2.5.2",
    "ng-recaptcha-2": "^15.0.2",
    "ngx-editor": "^18.0.0",
    "ngx-quill": "^27.0.0",
    "quill": "^2.0.3",
    "quill2-emoji": "^0.1.2",
    "rxjs": "~7.8.0",
    "tslib": "^2.8.1",
    "tw-elements": "^2.0.0",
    "zone.js": "^0.15.0"
  },

I also tried to set "preserveSymlinks": false, in angular.json however, the issue still persists.

@google-oss-bot
Copy link

This issue does not seem to follow the issue template. Make sure you provide all the required information.

@FidelNguyen
Copy link

Hi,

I have exactly the same issue.
I pass through by using following code await runInInjectionContext(this.injector, async () => { but I think it's a monkey patch that do not resolve the source.
Just want to know if there's a new way to implement or a real issue?

Thank you !

@codeneobee
Copy link

codeneobee commented Feb 17, 2025

I am having the same issue with version 19.0.0, it worked with the rc.0 release candidate of version 19 before though.

I have confirmed that version 19.0.0-rc.0 is the latest version without this issue, is there some new way to implement this that is not documented or is this a real issue?

@rgant
Copy link

rgant commented Feb 17, 2025

I believe that if you switch from the namespace syntax to the modular style that will solve the issue. https://firebase.google.com/docs/web/modular-upgrade

@djamn
Copy link
Author

djamn commented Feb 19, 2025

I believe that if you switch from the namespace syntax to the modular style that will solve the issue. https://firebase.google.com/docs/web/modular-upgrade

I hope there is still another solution, since my project is huge with multiple different services

@rgant
Copy link

rgant commented Feb 19, 2025

You could wrap the modular methods in an Angular Service. Still a large change, but mostly just renaming things hopefully. I can describe more if interested.

The only other solution is to wrap every Firebase method in an injection context.

Or, stick with older versions.

@djamn
Copy link
Author

djamn commented Feb 20, 2025

You could wrap the modular methods in an Angular Service. Still a large change, but mostly just renaming things hopefully. I can describe more if interested.

Would appreciate it if you could provide more information!

@rgant
Copy link

rgant commented Feb 20, 2025

Something like:

import {
  EnvironmentInjector,
  inject,
  Injectable,
  runInInjectionContext,
} from '@angular/core';
import { doc, Firestore } from '@angular/fire/firestore';
import type { DocumentReference } from '@angular/fire/firestore';

@Injectable({ providedIn: 'root' })
export class AngularFirestoreService {
  private readonly _firestore: Firestore = inject(Firestore);
  private readonly _injector: EnvironmentInjector = inject(EnvironmentInjector);

  /**
   * Note that the doc method could accept a CollectionReference or DocumentReference in addition to
   * Firestore.
   */
  public doc(path: string, ...pathSegments: string[]): DocumentReference {
    return runInInjectionContext(
      this._injector,
      (): DocumentReference => doc(this._firestore, path, ...pathSegments),
    );
  }
}

But note that I didn't test this code, it is off the cuff at 07:30 in the morning for me so it might be terrible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants