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

AngularFire v18 ng serve error when using zoneless change detection: Zone is not defined #3537

Open
anisabboud opened this issue May 23, 2024 · 19 comments

Comments

@anisabboud
Copy link

Version info

Angular: 18.0.0
AngularFire: 18.0.0
Firebase: 10.12.1

How to reproduce these conditions

Follow the Angular 18 guide to enable zoneless:

  1. Add provideExperimentalZonelessChangeDetection() to providers.
  2. remove zone.js from polyfills in angular.json.
  3. Run ng serve

https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe

Debug output

Error in the terminal Zone is not defined

/myproject/node_modules/@angular/fire/fesm2022/angular-fire.mjs:101
    this.outsideAngular = ngZone.runOutsideAngular(() => new ɵZoneScheduler(Zone.current));
                                                                            ^


ReferenceError: Zone is not defined
    at eval (/myproject/node_modules/@angular/fire/fesm2022/angular-fire.mjs:101:77)
    at input (/myproject/node_modules/@angular/core/fesm2022/core.mjs:15827:16)
    at _ɵAngularFireSchedulers (/myproject/node_modules/@angular/fire/fesm2022/angular-fire.mjs:101:34)
    at Object._ɵAngularFireSchedulers_Factory (/myproject/node_modules/@angular/fire/fesm2022/angular-fire.mjs:106:12)
    at eval (/myproject/node_modules/@angular/core/fesm2022/core.mjs:3233:47)
    at runInInjectorProfilerContext (/myproject/node_modules/@angular/core/fesm2022/core.mjs:871:9)
    at R3Injector.hydrate (/myproject/node_modules/@angular/core/fesm2022/core.mjs:3232:21)
    at R3Injector.get (/myproject/node_modules/@angular/core/fesm2022/core.mjs:3095:33)
    at injectInjectorOnly (/myproject/node_modules/@angular/core/fesm2022/core.mjs:1106:40)
    at ɵɵinject (/myproject/node_modules/@angular/core/fesm2022/core.mjs:1112:42)

Node.js v20.13.1

Expected behavior

AngularFire/ng serve should work with the new zoneless change detection introduced in Angular v18.
https://angular.dev/guide/experimental/zoneless

Actual behavior

ng serve crashes when opening localhost:4200.

@spock123
Copy link

spock123 commented May 23, 2024

@anisabboud it's because @angular/fire code expects zoneJS to be present. What you can do is this workaround (although not perfect solution): keep zoneJS in your polyfills, but still use zoneless change detection. This will work, but of course zoneJS will still be part of your initial payload. At least you won't be using it.
It would be nice if the code using zoneJS could check for existence of it before blindly using it. That would solve thing I guess.

But to be clear: we are running zoneless without issues - the only thing is that we have to keep zoneJS in polyfills, but that's it. It's still zoneless, but of course the initial payload is a tiny bit larger than it should have been. But this is a small price to pay to move to zoneless, we'll get to remove zonejs eventually I am sure.

@jamesdaniels
Copy link
Member

We're working to support Zoneless Angular in an upcoming release

@spock123
Copy link

spock123 commented May 23, 2024

@jamesdaniels did I say you rock? you do

@duxor
Copy link

duxor commented Jun 4, 2024

The same topic is here: #3494

@kiakahaDZ
Copy link

any solution found plz i have the same issue

@Benzilla
Copy link

Eagerly waiting on this - AngularFire is the only package currently stopping us from going zoneless

@wis-dev
Copy link

wis-dev commented Aug 12, 2024

@jamesdaniels any update about this? Maybe some release date.
I'm exiting to go Zoneless with Firebase 🔥

@Benzilla
Copy link

Slightly off-topic, but if you're trying to go Zoneless, you're probably doing it for performance gains.

I went through the process of converting my entire SSR Angular 18 app to use signals for zoneless change detection and was waiting on AngularFire to support zoneless so I could finally remove zone.js from polyfills to "officially" go completely zoneless.

In the process I realised that I no longer actually used any of the benefits of AngularFire, and decided to move over to the official Firebase package. This let my project go zoneless, and also reduces bundle size further and removes dependencies.

It's not something I'd considered because everytime I've built with Angular & Firebase, AngularFire was the first thing I reached for, but it seems maybe with these latest Angular developments it's not as needed? Either way, I've loved using this package and built some awesome stuff with it - thanks for all the support and work on it.

@anisabboud
Copy link
Author

@Benzilla can you please elaborate on the steps this migration entails?

For example, what's the replacement for the AngularFire initialization in app.config.ts?

export const appConfig: ApplicationConfig = {
  providers: [
    provideFirebaseApp(() => initializeApp({ ... })),
    provideFirestore(() => getFirestore()),
    provideAuth(() => getAuth()),
    provideFunctions(() => getFunctions()),
    provideStorage(() => getStorage()),
    provideAppCheck(() => ...),
    ...
  ],
  ...
})

@spock123
Copy link

spock123 commented Aug 15, 2024

@Benzilla agree, I am looking to do the same, I can't wait any longer now... it would be cool with a minimal example setup that can be used as a starting point - I scoured the Internet but didn't find a single example , unfortunately.

@muhamedkarajic
Copy link

muhamedkarajic commented Aug 15, 2024

Can someone help me I have following error:

Error: NG0908: In this configuration Angular requires Zone.js

import { enableProdMode, isDevMode, provideExperimentalZonelessChangeDetection } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from './app/app.module';

const providers: any[] = [
  provideExperimentalZonelessChangeDetection(),
];

if (!isDevMode()) {
  enableProdMode();
}

platformBrowserDynamic(providers)
  .bootstrapModule(AppModule)
  .catch(err => console.error(err));

When I keep zone.js in polyfills then it works. I also have @angular/localize/init in polyfills.

Any help? Maybe @spock123?

@Benzilla
Copy link

@Benzilla can you please elaborate on the steps this migration entails?

For example, what's the replacement for the AngularFire initialization in app.config.ts?

export const appConfig: ApplicationConfig = {
  providers: [
    provideFirebaseApp(() => initializeApp({ ... })),
    provideFirestore(() => getFirestore()),
    provideAuth(() => getAuth()),
    provideFunctions(() => getFunctions()),
    provideStorage(() => getStorage()),
    provideAppCheck(() => ...),
    ...
  ],
  ...
})

Despite AngularFire's docs, I don't think this is a good way to initialise Firebase or AngularFire anymore, and that they should be initialised in injectable services. Otherwise you are always including AngularFire / Firebase in your main bundle every single time which increases your initial chunk file transfer size and is bad for core web vitals.

Angular states this in their docs that this is the preferred way, and also states its good for tree shaking etc.

Anecdotally initialising them in services reduced my initial chunk file estimated transfer size from 209.78 kB down to 94.27 kB (over 50%!) and has shown a positive improvement to core web vitals. I think maybe the AngularFire docs shouldn't state initialising in ApplicationConfig is a good thing to do anymore?

In my project I have separate services:

  • database.service.ts for Firestore
  • storage.service.ts for Firebase Storage
  • api.service.ts for Firebase Functions
  • auth.service.ts for Firebase Auth
  • analytics.service.ts for Firbase Anaytics

In the constructor of each service I initialise firebase in a similar pattern, for example for Database:

import { Injectable } from '@angular/core';
import { initializeApp } from "firebase/app";
import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore';
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class DatabaseService {

  private firestore : any;

  constructor(){
    const app = initializeApp(environment.firebase);
    this.firestore = getFirestore(app);
    if (!environment.production) {
      if(!this.firestore._settingsFrozen){ //Prevents initialising the emulator twice on SSR.
        connectFirestoreEmulator(this.firestore, 'localhost', 8080);
      }
    }
  }
}

For Auth

import { Injectable } from '@angular/core';
import { getAuth, connectAuthEmulator } from 'firebase/auth';
import { initializeApp } from "firebase/app";
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class AuthProviderService {

  public auth: any;

  constructor(){
    const app = initializeApp(environment.firebase);
    this.auth = getAuth(app);
    if (!environment.production) {
      connectAuthEmulator(this.auth, 'http://localhost:9099', { disableWarnings: true })
    }
  }
}

I can then just import these services into any component and access the firebase objects, and Angular should optimise via the builder and tree shaking.

I struggled like @spock123 to find examples for Angular + official Firebase package online, as if you Google Angular + Firebase this package dominates the SERPs, so I'm not sure if this is the best way to do things - but I've been running it in a production app for a week now with no problems and improved performance overall.

@LanderBeeuwsaert
Copy link

@Benzilla ooooh that's a really interesting approach for lazy loading the packages.
We have similar services, as well as some landing & manual pages that don't need any firebase functionality.
So your solution is great for that!
Too bad indeed there is no guidance/documentation from angularFire about this.

@jits
Copy link

jits commented Aug 19, 2024

Give the conversations about using Firebase directly, without AngularFire, I hope folks don't mind me promoting the approach I've taken for the FullStacksDev Angular and Firebase tech stack: https://github.com/FullStacksDev/angular-and-firebase-template

You can get a quick overview of how I've set this up: https://github.com/FullStacksDev/angular-and-firebase-template/blob/main/ARCHITECTURE.md#app-accessing-firebase-services-from-the-angular-app. I then use rxfire (which AngularFire uses under the hood) directly in my services (to fit nicely with RxJS). Happy to take more questions etc. in the repo I linked to, if folks are interested.

@IsmaelBembo
Copy link

@Benzilla

In the constructor of each service I initialise firebase in a similar pattern, for example for Database:

I've been trying to do this for a long time before encountering your comment, which it looks like it should work but I can't get rid of this error:

image

This is my current state:

image

image

Any ideas?

@ciriousjoker
Copy link

@IsmaelBembo You need to switch from AngularFire to the regular firebase package.
@Benzilla Your approach doesn't seem to work well with SSR. For some reason, during SSR, the getDocs (replacement for collectionData) freezes completely. Had to switch back to AngularFire for that reason.

In general though: Maybe don't completely hijack this issue.

@dalenguyen
Copy link

@IsmaelBembo You need to switch from AngularFire to the regular firebase package. @Benzilla Your approach doesn't seem to work well with SSR. For some reason, during SSR, the getDocs (replacement for collectionData) freezes completely. Had to switch back to AngularFire for that reason.

In general though: Maybe don't completely hijack this issue.

I think you need to wrap it inside afterNextRender for it to work with SSR.

@Benzilla
Copy link

@Benzilla Your approach doesn't seem to work well with SSR. For some reason, during SSR, the getDocs (replacement for collectionData) freezes completely. Had to switch back to AngularFire for that reason.

@ciriousjoker Should work fine with SSR (my app is fully SSR).

You need to make sure your app is zoneless, and is using ExperimentalPendingTasks for SSR: https://angular.dev/api/core/ExperimentalPendingTasks

@riya-amemiya
Copy link

Is there any progress on this?
I'm looking forward to Angular Fire from Zoneless!

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