I'm working on a module federation angular nx project, with firebase and currently trying to get a header shared UI working with the Auth service.
The flow is basically:
Auth service in service library -> header in UI library -> host application
Auth.Service.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { UserModel } from '@veda/shared/model';
import {
Auth,
GoogleAuthProvider,
authState,
createUserWithEmailAndPassword,
sendEmailVerification,
updateProfile,
signInWithPopup,
} from '@angular/fire/auth';
import { doc, docData, setDoc } from '@angular/fire/firestore';
import { Firestore } from '@angular/fire/firestore';
import { Observable, of, switchMap } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class AuthService {
user$: Observable<UserModel | null>;
constructor(
private auth: Auth,
private afs: Firestore,
private router: Router
) {
this.user$ = authState(auth).pipe(
switchMap((user) => {
return user
? (docData(doc(this.afs, 'users', user.uid)) as Observable<UserModel>)
: of(null);
})
);
}
async emailSignUp(email: string, password: string) {
const credential = await createUserWithEmailAndPassword(
this.auth,
email,
password
);
await updateProfile(credential.user, {
displayName: credential.user.displayName,
});
await sendEmailVerification(credential.user);
return this.updateUserData(credential.user);
}
async googleSignIn() {
const provider = new GoogleAuthProvider();
await signInWithPopup(this.auth, provider)
.then((result) => {
const user = result.user;
this.updateUserData(user);
this.router.navigate(['/']);
})
.catch((error) => {
console.log(error);
});
}
async SignOut() {
await this.auth.signOut();
this.router.navigate(['/']);
}
private updateUserData(user: any) {
const userRef = doc(this.afs, 'users', user.uid);
const data = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
};
return setDoc(userRef, data, { merge: true });
}
}
shared-service.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { firebaseEnv } from '@veda/shared/enviroment';
import {
provideAnalytics,
getAnalytics,
ScreenTrackingService,
UserTrackingService,
} from '@angular/fire/analytics';
import { provideAuth, getAuth } from '@angular/fire/auth';
import { provideFirestore, getFirestore } from '@angular/fire/firestore';
import { provideFunctions, getFunctions } from '@angular/fire/functions';
import { provideMessaging, getMessaging } from '@angular/fire/messaging';
import { providePerformance, getPerformance } from '@angular/fire/performance';
import { provideStorage, getStorage } from '@angular/fire/storage';
import { AuthService } from './auth/auth.service';
@NgModule({
imports: [
CommonModule,
provideFirebaseApp(() => initializeApp(firebaseEnv)),
provideAnalytics(() => getAnalytics()),
provideAuth(() => getAuth()),
provideFirestore(() => getFirestore()),
provideFunctions(() => getFunctions()),
provideMessaging(() => getMessaging()),
providePerformance(() => getPerformance()),
provideStorage(() => getStorage()),
],
providers: [ScreenTrackingService, UserTrackingService, AuthService],
})
export class SharedServiceModule {}
header.component.html (just pasting a part of the use of the auth service.)
<div *ngIf="auth.user$ | async; then authenticated; else signin"></div>
header.component.ts
import { Component, Input } from '@angular/core';
import { NavlinkModel } from '@veda/shared/model';
import { AuthService } from '@veda/shared/service';
@Component({
selector: 'veda-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
})
export class HeaderComponent {
@Input() title = '';
@Input() navlinks: NavlinkModel[] = [];
constructor(public auth: AuthService) {}
shared-ui.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './header/header.component';
import { RouterModule } from '@angular/router';
import { AuthService } from '@veda/shared/service';
@NgModule({
imports: [CommonModule, RouterModule],
declarations: [HeaderComponent],
exports: [HeaderComponent],
providers: [AuthService],
})
export class SharedUiModule {}
app.component.html
<veda-header title="Veda" [navlinks]="navlinks"></veda-header>
<router-outlet></router-outlet>
app.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'veda-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
@Input() navlinks = [{ path: '', name: 'Home', guard: false }];
}
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { appRoutes } from './app.routes';
import { SharedUiModule } from '@veda/shared/ui';
import { LandingComponent } from './landing/landing.component';
import { AuthComponent } from './auth/auth.component';
import { AuthService } from '@veda/shared/service';
@NgModule({
declarations: [AppComponent, LandingComponent, AuthComponent],
imports: [
BrowserModule,
SharedUiModule,
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
],
providers: [AuthService],
bootstrap: [AppComponent],
})
export class AppModule {}
I being trying for a good part of the day, the code itself does seem to find the AuthService and no errors are displayed in linting or in vscode, but when I serve the host using nx serve host --open
The console displayed the error code
NullInjectorError: R3InjectorError(AppModule)[AuthService -> Auth -> Auth -> Auth]:
NullInjectorError: No provider for Auth!
Angular 6
AuthService_Factory auth.service.ts:21
Angular 6
HeaderComponent_Factory header.component.ts:10
Angular 5
AppComponent_Template app.component.html:1
Angular 31
webpackJsonpCallback jsonp chunk loading:77
<anonymous> Angular
Not sure why this is happening, hopefully someone can help me.
Tried to export just the Auth service and use it in the correct places, tried using
@Injectable({
providedIn: 'root',
})
and adding the provider and shared-service module to the libraries and apps that consume auth service