Inject() must be called from an injection context such as a constructor in AngularFirestoreDoc after upgrading to Angular v19
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.
This issue does not seem to follow the issue template. Make sure you provide all the required information.
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 !
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?
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 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
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.
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!
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.
So it works when migrating namespace syntax to modular syntax. In my case it would be the following:
private auth: Auth;
private firestore: Firestore;
isLoggedIn$: Observable<boolean>;
user$: Observable<User | null | undefined>;
userRoles$: Observable<string[]>;
userId: string | undefined = undefined;
username: string | undefined = undefined;
constructor(firestore: Firestore, private router: Router) {
this.auth = getAuth();
this.firestore = firestore;
this.user$ = new Observable<User | null | undefined>((observer) => {
onAuthStateChanged(this.auth, async (user) => {
if (user) {
this.userId = user.uid;
const userDocRef = doc(this.firestore, `users/${user.uid}`);
docData(userDocRef).subscribe((userData) => {
if (userData) {
this.username = (userData as User).username;
}
observer.next(userData as User | null);
});
} else {
this.userId = undefined;
this.username = undefined;
observer.next(null);
}
});
});
However, this is still a very breaking change to all services and I did not find anything in the changelog.
I think it's really important not to loose sight of this issue. We are stuck not able to upgrade to Angular 19 because of this issue. We're not ready to move off the compat API yet.
Regardless, the v19 release of this library says that it supports the compat API when in fact that just seems broken. If the library is not willing to fix the issue with the compat API, than it needs to remove support for it altogether and not gaslight the community.
Now Angular V20 is out wondering what issues will come about
I lost whole 1 day, figuring out it simply doesn't work. But as far as I read somewhere, it should be supported. So we can not move to Angular 19, without migrating everything to modules SDK, period.
Hello I am in the same position. I am moving from angular 16 to 19 and it seems this version of angular fire compat gives this above error. Our codebase is quite large and a move off of compat sdk would be timely. Is there any update or solution to this problem? Seems like the solutions in here are either use RunInInjectionContext or move to modular. Neither of which is clean.
This worked for me. I wasted way too much time on this. https://github.com/angular/angularfire/pull/3590#issuecomment-2581455741
I'm also currently facing this issue during unit testing (Karma/Jasmine) and it seems that this problem only happens for Angular 19.
In Angular 20, this problem doesn't seem to occur.
This ussue still occurs for Angular version 20.1 and @angular/fire version 20.0.1
Finally I had to migrate my project to the new modular API, which works fine with ng 20.1. But still I should not be required to do this.
How painful was it @tscislo ? Did you need to refactor a lot?
@sasos90 it wasn't very painful, but I had to refactor all use cases of angular/fire API. So it depends on your code base, mine wasn't that big. Good thing is that I was able to replace all use cases of the old API with the new modular one, so there were no gaps in functionality.
@sasos90 it wasn't very painful, but I had to refactor all use cases of angular/fire API. So it depends on your code base, mine wasn't that big. Good thing is that I was able to replace all use cases of the old API with the new modular one, so there were no gaps in functionality.
Would you mind sharing an example of the refactoring into the the new modular API call? Just want to make sure I'm on the same page. Thank you!
Moving to modular was mostly fine for me. But Analytics was a pain. Basically all custom features got removed. Might as well just use straight firebase a this point.
@syamanashi Basically you need to get rid of everything that comes from @angular/fire/compat namespace with equivalents. I didn't use Analytics just data storage and mutation capabilities and it was fine for me.
Looks like this may have been fixed with v19.0.0-rc.5. Have not tried though. https://github.com/angular/angularfire/releases/tag/19.0.0-rc.5
Mixed reports above about this occurring in later versions.