angular - Hur man delar AngularFire-inställning mellan ett projekt och ett inkluderat anpassat bibliotek
Hittills har jag ett Angular 7-projekt, som har ett par moduler i sig självt och även mitt anpassade bibliotek med outsourcad gemensam funktionalitet, och både projektet och biblioteket måste använda AngularFire. Problemet som jag får när jag försöker göra nästa uppsättning är att jag får ett fel som detta när jag besöker min webbplats från webbläsaren.
Error: StaticInjectorError(AppModule)[AngularFirestore -> InjectionToken angularfire2.app.options]:
StaticInjectorError(Platform: core)[AngularFirestore -> InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
at NullInjector.get (core.js:8896)
at resolveToken (core.js:9141)
at tryResolveToken (core.js:9085)
at StaticInjector.get (core.js:8982)
at resolveToken (core.js:9141)
at tryResolveToken (core.js:9085)
at StaticInjector.get (core.js:8982)
at resolveNgModuleDep (core.js:21218)
at _createClass (core.js:21271)
at _createProviderInstance (core.js:21235)
Här är uppställningen av modulerna:
app.modul.ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import { AppRoutingModule } from "./app-routing.module";
import { CoreModule, firebaseConfig } from "./core/core.module";
import { SharedModule } from "./shared/shared.module";
import { FormsModule } from "@angular/forms";
import { HttpClient, HttpClientModule } from "@angular/common/http";
import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { NgPipesModule } from "ngx-pipes";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { ChartModule, HIGHCHARTS_MODULES } from "angular-highcharts";
import { ScrollToModule } from "ng2-scroll-to-el";
import { NgCircleProgressModule } from "ng-circle-progress";
import { AngularFireModule } from "@angular/fire";
import { AngularFireAuthModule } from "@angular/fire/auth";
import { AngularFireStorageModule } from "@angular/fire/storage";
import { AngularFirestoreModule } from "@angular/fire/firestore";
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http);
}
declare var require: any;
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
SharedModule,
CoreModule,
FormsModule,
NgPipesModule,
HttpClientModule,
BrowserAnimationsModule,
NgbModule,
ChartModule,
AngularFireModule.initializeApp(firebaseConfig),
AngularFireAuthModule,
AngularFireStorageModule,
AngularFirestoreModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
NgCircleProgressModule.forRoot({}),
ScrollToModule.forRoot()
],
providers: [
{
provide: HIGHCHARTS_MODULES,
useFactory: () => [require("highcharts/highcharts-more.src")]
} // add as factory to your providers
],
bootstrap: [AppComponent]
})
export class AppModule {}
Core.module.ts:
import {
Compiler,
COMPILER_OPTIONS,
CompilerFactory,
NgModule
} from "@angular/core";
import { JitCompilerFactory } from "@angular/platform-browser-dynamic";
import { registerLocaleData } from "@angular/common";
import localeUk from "@angular/common/locales/uk";
registerLocaleData(localeUk);
import { HeaderComponent } from "./header/header.component";
import { AppRoutingModule } from "../app-routing.module";
import { SharedModule } from "../shared/shared.module";
import { CoreRoutingModule } from "./core-routing.module";
import { MainAreaComponent } from "./main-area/main-area.component";
import { TranslateModule } from "@ngx-translate/core";
import { StrengthsService } from "./strengths.service";
import { ScoreCardsService } from "./score-cards.service";
import { GroupsService } from "./groups.service";
import { StudentsService } from "./students.service";
import { TutorsService } from "./tutors.service";
import { SubjectsService } from "./subjects.service";
import { ConditionsService } from "./conditions.service";
import { ChartsDataService } from "./charts/charts-data.service";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { SettingsService } from "./settings.service";
import { AuthService, TCoreModule } from "@eriendel/t-core";
import { environment } from "../../environments/environment";
export function createCompiler(fn: CompilerFactory): Compiler {
return fn.createCompiler();
}
export const firebaseConfig = environment.firebaseConfig;
@NgModule({
declarations: [HeaderComponent, MainAreaComponent],
imports: [
SharedModule,
AppRoutingModule,
CoreRoutingModule,
TranslateModule.forChild(),
NgbModule,
TCoreModule.forChild({
autoAuthRoleCredentials: environment.autoAuthRoleCredentials
})
],
exports: [AppRoutingModule, HeaderComponent],
providers: [
{
provide: COMPILER_OPTIONS,
useValue: {},
multi: true
},
{
provide: CompilerFactory,
useClass: JitCompilerFactory,
deps: [COMPILER_OPTIONS]
},
{
provide: Compiler,
useFactory: createCompiler,
deps: [CompilerFactory]
},
ScoreCardsService,
StrengthsService,
GroupsService,
StudentsService,
TutorsService,
SubjectsService,
ConditionsService,
ChartsDataService,
SettingsService,
AuthService
]
})
export class CoreModule {}
Och modulen i mitt bibliotek t-core.module.ts:
import { ModuleWithProviders, NgModule, LOCALE_ID } from "@angular/core";
import { HTTP_INTERCEPTORS, HttpClient } from "@angular/common/http";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { RouterModule } from "@angular/router";
import { CommonModule, registerLocaleData } from "@angular/common";
import localeUk from "@angular/common/locales/uk";
registerLocaleData(localeUk);
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { LoggingInterceptor } from "./logging.interceptor";
import { NotFoundComponent } from "./not-found/not-found.component";
import { SidebarComponent } from "./sidebar/sidebar.component";
import { AuthService } from "./auth.service";
import { DatesService } from "./dates.service";
import { LocalizationService } from "./localization.service";
import { CalculationsService } from "./calculations/calculations.service";
import { NavService } from "./nav.service";
import { SigninComponent } from "./auth/signin/signin.component";
import { ChangePassComponent } from "./auth/change-pass/change-pass.component";
import { AntiAuthGuard } from "./auth/antiAuth.guard";
import {
AuthGuard,
AutoAuthRoleCredential,
AUTO_AUTH_ROLE_CREDENTIALS
} from "./auth.guard";
import { FormsModule } from "@angular/forms";
import { HttpLoaderFactory } from "./HttpLoaderFactory";
import { UIModule } from "./ui/ui.module";
import { AngularFireModule, FirebaseAppConfig } from "@angular/fire";
import { AngularFireAuthModule, AngularFireAuth } from "@angular/fire/auth";
import {
AngularFirestoreModule,
AngularFirestore
} from "@angular/fire/firestore";
import { AngularFireStorageModule } from "@angular/fire/storage";
export interface TCoreModuleConfig {
autoAuthRoleCredentials: AutoAuthRoleCredential[];
}
// @dynamic
@NgModule({
declarations: [
NotFoundComponent,
SidebarComponent,
SigninComponent,
ChangePassComponent
],
imports: [
RouterModule,
CommonModule,
FormsModule,
UIModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
NgbModule
],
exports: [
SidebarComponent,
NotFoundComponent,
SigninComponent,
ChangePassComponent
]
})
export class TCoreModule {
static forRoot(config: TCoreModuleConfig): ModuleWithProviders {
return {
ngModule: TCoreModule,
providers: [
AuthService,
CalculationsService,
DatesService,
LocalizationService,
NavService,
AntiAuthGuard,
AuthGuard,
{
provide: AUTO_AUTH_ROLE_CREDENTIALS,
useValue: config.autoAuthRoleCredentials
},
{
provide: HTTP_INTERCEPTORS,
useClass: LoggingInterceptor,
multi: true
},
{
provide: LOCALE_ID,
deps: [LocalizationService],
useFactory: localizationService =>
localizationService.getLocale()
},
AngularFirestore,
AngularFireAuth
]
};
}
static forChild(config: TCoreModuleConfig): ModuleWithProviders {
return this.forRoot(config);
}
}
Så problemet här, som jag ser det, är att jag inte har någon möjlighet att initiera AngularFireModule i mitt underliggande bibliotekmodul. För det skulle jag behöva ha Firebase-konfigurationen tillgänglig i importsektionen av t-core-modulen och det skulle bara kunna injiceras i forRoot()-metoden i t-core modulklass, vilket är för sent för att göra importen såvitt jag vet.
Finns det något annat sätt att få detta att fungera?
Svar #1
Jag hade också problem med att få detta att fungera. Jag hittade en överraskande lösning. Här är en minimal replikation av den lösningen.
Kort sagt, jag initialiserar inte och inkluderar inte ens Angular Fire direkt i bibliotekmodulen. Jag initialiserar och inkluderar det endast i applikationsmodulen. Det verkar som att bibliotekmodulen automatiskt kan dra nytta av Angular Fire när allt importeras till och kompileras med applikationsmodulen.
Här är en vy av den relevanta koden:
Biblioteksmodul
shared.module.ts
Jag förväntade mig att behöva initialisera Angular Fire i denna fil, men varje försök att göra det ledde till misslyckande. Till slut försökte jag bara inkludera AngularFireAuth, AngularFirestore och AngularFireDatabase modulerna från Angular Fire och inte basmodulen AngularFireModule. Det fungerade. Sedan försökte jag ta bort alla AngularFire-moduler från biblioteket och det fungerade också. Dessa verkar fungera som peer-beroenden, där biblioteket inte behöver inkludera Angular Fire-modulberoendena direkt i sin egen modul, så länge de är inkluderade i den modul som faktiskt körs.
import { NgModule } from '@angular/core'
@NgModule({
declarations: [],
imports: [
],
exports: []
})
export class SharedModule { }
shared.service.ts
Den här filen utnyttjar AngularFireAuth-tjänsten, även om AngularFireAuthModule inte har inkluderats i bibliotekets modul.
import { Injectable } from '@angular/core'
import { AngularFireAuth } from '@angular/fire/auth'
import { map } from 'rxjs/operators'
@Injectable({
providedIn: 'root'
})
export class SharedService {
constructor(
private fireAuth: AngularFireAuth
) { }
getClaims$() {
return this.fireAuth.idTokenResult.pipe(
map(idTokenResult => (idTokenResult || { claims: null }).claims)
)
}
}
Applikationsmodul
app.module.ts
I den här filen initialiserar och inkluderar vi slutligen AngularFireModule, samt bibliotekmodulen (SharedModule). Av någon anledning behövde jag aldrig inkludera AngularFireAuthModule för att få detta att fungera.
import { AppComponent } from './app.component'
import { SharedModule } from 'shared'
import { AngularFireModule } from '@angular/fire'
import { environment } from '../environments/environment'
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AngularFireModule.initializeApp(environment.firebase),
SharedModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
I den här filen använder vi tjänsten från bibliotekmodulen (SharedService) för att hämta användaranspråk från AngularFireAuth-tjänsten.
import { Component } from '@angular/core'
import { Observable } from 'rxjs'
import { SharedService } from 'shared'
@Component({
selector: 'app-root',
styles: [],
template: `
<h1>Hello.</h1>
<pre *ngIf="(claims$ | async) as claims">let claims = {{ claims | json }}</pre>
`
})
export class AppComponent {
claims$: Observable<any>
constructor(
private auth: SharedService
) { }
ngOnInit() {
this.claims$ = this.auth.getClaims$()
}
}
Svar #2
Har du satt upp Firebase-miljö i din Angular-app? 1) Gå till din Firebase-konsol 2) välj projekt -> projektinställning 3) Där får du något som detta: -> var firebaseConfig-objekt därifrån
4) Kopiera det och klistra in det i din angular_project_folder->src->environments->environment.ts på detta sätt
export const environment={obj}
