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

Lazy loading FirebaseUIModule #19

Closed
mlejva opened this issue Aug 30, 2017 · 8 comments
Closed

Lazy loading FirebaseUIModule #19

mlejva opened this issue Aug 30, 2017 · 8 comments

Comments

@mlejva
Copy link

mlejva commented Aug 30, 2017

Using custom modules that are being lazy loaded and that import FirebaseUIModule results in Error: An AuthUI instance already exists for the key "[DEFAULT]" Error: An AuthUI instance already exists for the key "[DEFAULT] error.

Consider this scenario

app.module.ts

import { environment }    from '../environments/environment';
import { AppComponent }   from './app.component';

import { AngularFireModule }      from 'angularfire2';
import { AngularFireAuthModule }  from 'angularfire2/auth';
import { FirebaseUIModule }       from 'firebaseui-angular';

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    FirebaseUIModule.forRoot(environment.firebaseUiConfig),    
    AppRouting
  ],
  declarations: [
    AppComponent
  ],
  providers: [ ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

app.routing.ts

import { NgModule }             from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
  { path: 'login',        loadChildren: 'app/login/login.module#LoginModule'  },
  { path: 'register',   loadChildren: 'app/register/register.module#RegisterModule' }

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ],
})
export class AppRouting { }

Now the login and register feature. Each having their own module.

login.module.ts

import { environment }    from '../../environments/environment';
import { NgModule }     from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule }  from '@angular/forms';

import { FirebaseUIModule } from 'firebaseui-angular';

import { LoginComponent } from './login.component';
@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        FirebaseUIModule
    ],
    declarations: [
        LoginComponent
     ],
    exports: [ LoginComponent ],
})
export class LoginModule { }

register.module.ts

import { environment }   from '../../environments/environment';
import { NgModule }     from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule }  from '@angular/forms';

import { FirebaseUIModule } from 'firebaseui-angular';

import { RegisterComponent } from './register.component';
@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        FirebaseUIModule
    ],
    declarations: [
         RegisterComponent
     ],
    exports: [ RegisterComponent ],
})
export class RegisterModule { }

When the app redirects to either /login or /register for the first time Firebase UI is presented without any problem. If the first visited path is visited next time (without visiting the other one) UI is also presented without any problem (e.g. user visits /login consecutively several times). On the other hand if the second visited path is not same as the first visited path (e.g. user visits first /login and then visits /register) the error is thrown.

Full error message

ERROR Error: Uncaught (in promise): Error: An AuthUI instance already exists for the key "[DEFAULT]"
Error: An AuthUI instance already exists for the key "[DEFAULT]"
    at new Dl (npm.js:334)
    at new FirebaseUIService (index.js:15)
    at _createClass (core.es5.js:9520)
    at _createProviderInstance$1 (core.es5.js:9492)
    at resolveNgModuleDep (core.es5.js:9477)
    at NgModuleRef_.webpackJsonp.../../../core/@angular/core.es5.js.NgModuleRef_.get (core.es5.js:10569)
    at resolveDep (core.es5.js:11072)
    at createClass (core.es5.js:10931)
    at createDirectiveInstance (core.es5.js:10756)
    at createViewNodes (core.es5.js:12197)
    at new Dl (npm.js:334)
    at new FirebaseUIService (index.js:15)
    at _createClass (core.es5.js:9520)
    at _createProviderInstance$1 (core.es5.js:9492)
    at resolveNgModuleDep (core.es5.js:9477)
    at NgModuleRef_.webpackJsonp.../../../core/@angular/core.es5.js.NgModuleRef_.get (core.es5.js:10569)
    at resolveDep (core.es5.js:11072)
    at createClass (core.es5.js:10931)
    at createDirectiveInstance (core.es5.js:10756)
    at createViewNodes (core.es5.js:12197)
    at resolvePromise (zone.js:795)
    at resolvePromise (zone.js:766)
    at zone.js:844
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:425)
    at Object.onInvokeTask (core.es5.js:3881)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (zone.js:192)
    at drainMicroTaskQueue (zone.js:602)
    at <anonymous>

The error might be caused by the fact that every time FirebaseUIModule is imported then also firebaseui.service is loaded.

  • When a module is loaded normally it is registered in the root app injector and a service that module is also loading is injected. Now Angular can find the given service in the app root injector and provides a singleton for the whole app. No new service provider instances are created.

  • When a module is lazy-loaded a child injector is created and the given service is registered with that child injector.
    Now, when lazy component is created it must inject the given service. The given service provider is in child injector of that lazily-loaded module. So a new instance of the service is created. This happens for each lazily-loaded module. Here it causes to create multiple AuthUI instances.

Full explanation is given here - https://angular.io/guide/ngmodule-faq#q-why-bad

The solution might be to want from developer to import firebaseui.service in the app.module and put it in providers so the service is available globally and not load firebaseui.service in firebaseui.module?

@RaphaelJenni
Copy link
Owner

Sorry for my late response. I will check that.

@RaphaelJenni
Copy link
Owner

OK, I could reproduce it. I'm now trying to solve it. Will keep you updated.

RaphaelJenni added a commit that referenced this issue Sep 8, 2017
- store the firebaseui instance on the window object to prevent double initialization
@RaphaelJenni
Copy link
Owner

Ok, I found a fix. Version 0.4.4 is live now. Please give it a try and tell me if it solved the issue.

@mlejva
Copy link
Author

mlejva commented Sep 8, 2017

Thank you. I'm not sure whether I'll have time today to try it. I'll inform you soon though.

@mlejva
Copy link
Author

mlejva commented Sep 11, 2017

I tested it today and it seems that everything is working properly. Thank you for the fix and sorry for the late response.

@RaphaelJenni
Copy link
Owner

Perfect. So I close the issue.

@jkarttunen
Copy link

For anyone else arriving here by searching the error message. This is what you need to do:
365b8d4#diff-327e415746b172f4c43dbcfbbd7046d1R15

(Global variable is not necessary, you can prevent double initialization also with a variable in outer enough scope)

@RaphaelJenni
Copy link
Owner

@jkarttunen I'm not sure what you mean with that? This is a commit I did and the newest versions has this fix already in it...

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

3 participants