Closed xmlking closed 6 years ago
Interesting you would create a Core module in libs. To me, libs should contain the supporting modules, services, components, etc. But maybe it's just naming...
Anyways, for me, I use InjectionToken to provide the environment or a slice of it. And then in the libs modules/service, I inject the environment there. like:
https://github.com/dereklin/nx-demo-jest/blob/lazy-5/apps/app1/src/app/services/index.ts
Good idea @dereklin thanks.
I had a question on best practice where to keep shared
and core
modules , and looking for opinion.
My reasoning for sharing them is , in most cases I have similar shared
modules for all my apps.
https://twitter.com/xmlking/status/953812533046996992?ref_src=twcamp%5Eshare%7Ctwsrc%5Em5%7Ctwgr%5Eemail%7Ctwcon%5E7046%7Ctwterm%5E1
If your meaning of Core to be equivalent of Shared, then it makes sense. I understand/interpret Core differently. To me, Core is the Core of the App and should live inside the App.
I think it makes sense to make a ShareModule and put all the shared stuff there, components, services, etc. And put the ShareModule in libs. But I would also use index.ts file to export individual items in the libs folder so that they can be located flatly and easily. One consideration if at some point you may need to deploy a component to npm or some internal package system to lock down the version. So you need to think about how to address these situations.
I am using ShareModule
to encapsulate Angular and commonly used 3rd party modules (Material Design, flex-layout, Angular's CommonModule, FormsModule etc) and importing ShareModule
into each Lazy-loaded Feature Modules
(which are also under lib
)
My CoreModule
consists shared Services
, and some Components
that are only used by AppModule
. This CoreModule
is imported only into AppModule
, basically to keep AppModule
slim.
I am still modeling reusable services/components as Modules
under lib
This is described in Angular Style Guide and here
Wanted to get other peoples opinion on this approach: is still valid for monorepo
/nx
paradigm!
Keep in mind that the whole libs folder is already shared between apps. And in general, making small units will help code reusability. And making small units usually means making these units flat. The downside of a flat structure is when you have a thousand units, it becomes difficult to find what you are looking for. This is art rather than science. I think we have to make trade-offs and pick the optimal solution for the project and team.
Are you lazy loading the FeatureModule with a route? If so, is the SharedModule only available to FeatureModule? Or how do you lazy load a FeatureModule without using a route?
here is my experimental workspace setup: https://github.com/xmlking/nx-starter-kit
step-by-step instructions tor reproduce: https://github.com/xmlking/nx-starter-kit/blob/master/PLAYBOOK.md
Still have some issues with some ng generators
, I will open separate issues.
PS: moved CoreModule
to apps/default
app
I fixed following long-path warnings that TSlint
and intelliJ
is complaining by following @tomastrajan blog's best praticies.
Before
//some code in lib's modules
import {environment} from "../../../apps/default/src/environments/environment";
import { PageTitleService } from '../../../../../apps/default/src/app/core/services/page-title/page-title.service';
After
import { environment } from '@env/environment';
import { PageTitleService } from '@default/core';
my tsconfig.json
now looks as:
"paths": {
"@nx-starter-kit/*": [
"libs/*"
],
"@default/*": ["apps/default/src/app/*"],
"@env/*": ["apps/default/src/environments/*"]
}
Should nx-cli
automatically do this for us?
I think that the issue here as described by the @dereklin is that the stuff which goes to libs
is supposed to be reusable in a "standalone" manner. That means it should not really depend directly on other parts of application.
CoreModule
is not a very good candidate for extracting into separate standalone lib becouse is heavily coupled with the app itself and is used in the most cases in the AppModule as mentioned above.
Example of libs
style functionality can be a stateless service which does some calculation or transformation of data. The key here is that the service SHOULD receive everything from the caller.
In your example that means that only the app imports environment ( could very well do it with TS path aliases like @env/environment
) but then passes env properties to the libs
service during the function call as a parameter.
That way libs
functionality remains stateless and standalone and hence more reusable.
Hope that helps! ๐
Importing from apps could create code entanglement issues
@xmlking - Defining an @env/*
path is an interesting idea. Perhaps we should add this to the schematics. And document using environment
in libs files.
Imo the best way to share env variables is to do something like this:
import {config} from '../environments/environment';
@NgModule({
providers: [
{
provider: 'someEndpointUrl', useValue: config.someEndpointUrl
}
]
})
class AppModule {
}
Then do this somewhere in your lib:
@Component({...})
class MyComponent {
constructor(@Inject('someEndpointUrl') endpointUrl: string) {
}
}
Using @env/
has some advantages though. It allows to use to improve treeshaking in some situations.
@vsavkin The problem with this approach is that you can't make the most of tree shaking. Let's say you want to use a development module when running locally and a production noop module instead when building for production - you would want the development module tree shaken out of prod builds. You want to be able to do something like this:
import { prod } from 'environments';
const DevelopModule = prod ? NoopModule : DevelopmentModule;
@NgModule({
imports: [DevelopModule],
//...
})
export class MyModule {}
Which should cause DevelopmentModule
to get shaken out, i.e. what you want (imagine it's quite large with a lot of test data).
@env/
seems to be the only way to do this in a libs
module that I can see.
Hi! @xmlking - did everything as described above. Getting this error when building my lib:
`BUILD ERROR error TS6059: File 'E:/repos/Ins/Ins.Web/approot/src/environments/environment.ts' is not under 'rootDir' 'E:\repos\Ins\Ins.Web\approot\projects\ins\src'. 'rootDir' is expected to contain all source files.
Am I missing something? Using Angular + CLI and etc v 7.1.0. Thx in advance!
I really would love to use "@env", can't make it work.
So far it is working for me. Example project https://github.com/xmlking/ngx-starter-kit
The @env/*
: ['some-place/*'] works well with Angular apps, but it doesn't work with node-api.
I have to use @env/environment
: ['some-place/environment.ts'] for the node-api to build.
Any idea why ?
@GrandSchtroumpf I am using import { environment as env } from '@env-api/environment';
check here in case you need example
https://github.com/xmlking/ngx-starter-kit/blob/develop/apps/api/src/app/notifications/subscription/subscription.service.ts
I thought you guys would find this useful: Using Abstract Classes As Dependency-Injection Tokens For Swappable Behaviors
I have 3 Angular apps in an Nx project that share some common environment variables. A public site, a private portal, and an admin app. In my libs
, I am using an abstract Environment
class that defines common environment variables.
export abstract class Environment {
abstract readonly production: boolean;
abstract readonly appUrls: {
readonly public: string;
readonly portal: string;
readonly admin: string;
};
}
Then I changed the 3 environment.ts
files to be as follows.
import { Environment } from '@my-lib-prefix/common';
class EnvironmentImpl implements Environment {
production = false;
appUrls = {
public: 'http://localhost:4200',
portal: 'http://localhost:4201',
admin: 'http://localhost:4202'
};
}
export const environment = new EnvironmentImpl();
The environment.prod.ts
would of course be symmetric to the dev environment.ts
. Then I provide the respective environment dependency, in the root app.module.ts
for each of the Angular apps.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Environment } from '@my-lib-prefix/common';
import { environment } from '../environments/environment';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [{ provide: Environment, useValue: environment }],
bootstrap: [AppComponent]
})
export class AppModule {}
Now any component can inject environment dependencies in a clean, and common manner.
import { Component } from '@angular/core';
import { Environment } from '@my-lib-prefix/common';
@Component({
selector: 'my-login',
templateUrl: './my-login.component.html'
})
export class MyLoginComponent {
constructor(private env: Environment) {}
}
It enforces each environment.ts
to implement the "common" environment variables defined in the abstract class. Also, each respective EnvironmentImpl
can be extended with their own specific environment variables specific to the app. This approach seems very flexible and clean. Cheers! ^_^
@ZenSoftware I do like the approach you presented. My only contribution would be that instead of using a class for representing the Configuration contract, you could use as well an interface with the caveat you would need to create an InjectionToken for that interface as lookup key. Either way you instantiate something, your Config Class or the InjectionToken for the interface.
@rsyhanes I myself was actually trying to use TypeScript Interfaces to solve this problem. Then I learned how to use TypeScript Abstract Classes as Dependency Injection Tokens. This approach seems to have the benefit of injecting dependencies via the class name, instead of an InjectionToken
. Environment variables are often used by many members of a team. Keeping its use as simple as possible should help to ensure the code base remains sustainable. (โฉ^o^)โโโใ.*
It seems like all these @env/
examples reference a specific default
app. Is it possible to get the current app instead? Or in other words, get the app that's currently invoking this lib.
Create as many named environments you need in root tsconfig.json
Generally I will have one for each app in /apps
"@env-api/environment": ["apps/api/src/environments/environment.ts"],
"@env-webapp/environment": ["apps/webapp/src/environments/environment.ts"],
The problem is that the lib shouldn't have to reference just one app explicitly. If I have 3 apps that all use the same lib, how will the lib know which environment to use?
@Josh-Hicks agree. may be we need a common env
that is independent of all apps. lib/shared/src/environments/environment.ts
?
other idea: multiple configs merging at runtime from :
https://twitter.com/yurzui/status/1138816561848995841
Sounds like this specific problem may not be possible to solve.
// in lib import {environment} from '...'; // <-- this is unknown at this point because it could be used in ANY app const googleMapsParams: { myKey: environment.key // <-- specific to the app that's using this lib } @NgModule({ imports: [ AgmCoreModule.forRoot(googleMapsParams) // <-- this needs an environment config ]}) export class MyLibModule {}
This tweet explains the issue. https://twitter.com/MannIsaac/status/1139257629769961475
Maybe you can solve it by using forRoot and dynamically returning a given module setup from the lib module itself. I came up with this a little while ago so my lib would know not to import a module used to provide an in memory api. I will provide example shortly (not at my machine).
I wrote it up on my website actually, go here and select the dynamic modules article.
http://www.tcoz.com/newtcoz/#/errata
The trick is to feed in the env via forRoot, so whatever is loading it becomes the relevant env. You can then switch on the env data and return a given setup. It was one piece of the ideal mono repo picture nx didn't seem to solve.
Extending and tinkering with this notion,I've managed a few interesting things that don't seem otherwise possible.
I have stumbled upon this issue some time ago. In my case I have multiple frontend apps (web and ionic) sharing the same config, and multiple backend apps (node/express and firebase functions) sharing the same config. After giving it some though I have decided on a solution that would employ angular.cli
.
environments
folder from each app.tsconfig.json
add paths:
"@env/frontend": ["environments/frontend/environment.ts"],
"@env/backend": ["environments/backend/environment.ts"],
angular.cli
replace:
"fileReplacements": [
{
"replace": "apps/web-app/src/environments/environment.ts",
"with": "apps/web-app/src/environments/environment.prod.ts"
}
],
with:
"fileReplacements": [
{
"replace": "environments/frontend/environment.ts",
"with": "environments/frontend/environment.prod.ts"
}
],
This will let you use your environment as you would normally in apps and libs. The only downside is that you don't have any health checks that would prevent you from importing backend environment in frontend lib/app. I don't imagine myself doing that so I settled on this solution.
Still if you worry that it may be a problem for you then you could still use this solution but not provide this environment in the root and through tsconfig paths but create separate libs for frontend and backend environments and then use nx taging to disallow import from those paths in certain apps/libs.
Hope it helps.
There's a relatively simple way to make the environment object available to a shared service.
After passing the service into a component constructor, set an "environment" property on the instance of the service, rather than trying to set it on the class.
Service
@Injectable({
providedIn: 'root'
})
export class Service {
environment: any;
aFunctionThatUsesEnvironment() {
console.log(this.environment);
}
}
Component
import { environment } from '../../environments/environment';
export class Component {
constructor(private service: Service) {
this.service.environment = environment;
this.service.aFunctionThatUsesEnvironment();
}
}
The environment object is printed to the console when the component is called.
I don't really understand all the discussion here. IMO Victor's solution is the best approach.
Your libraries/classes should NOT depend on the environment directly in your classes, you should use InjectionTokens instead, for all the reasons Victor already stated.
It's not hard to use InjectionTokens anyway, so there is no real good reason to do it any other way.
You're absolutely right; you don't really understand all the discussion. The solution you refer to would not resolve all contexts, certainly not the one I encountered.
On Jul 1, 2019, at 3:01 AM, michelcve notifications@github.com wrote:
I don't really understand all the discussion here. IMO Victor's solution is the best approach.
Your libraries/classes should NOT depend on the environment directly in your classes, you should use InjectionTokens instead, for all the reasons Victor already stated.
It's not hard to use InjectionTokens anyway, so there is no real good reason to do it any other way.
โ You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.
@tcoz Perhaps I don't, but I haven't seen an example yet, where it is really required to have your environment available in your module. There might be edge cases, as Victor stated, but usually it's bad architecture.
My main point being is that the specific question by XMLKing was aswered by Victor. In my opinion, specific corner cases are best discussed in a seperate issue (where you even might have a chance of Nrwl joining in on the discussion), and keeping this thread 'clean', and the proper solution (for most cases) easily found.
But alas, we're beyond that point now, aren't we ;-).
As you're sure to bring up your specific problem, I've taken a look at your site.
Correct me if I'm wrong, but basically you want to load a different module, depending on some environment flag.
In my opinion, that kind of customization is part of your application (hence it is configured there). As such, you should also do the conditional loading of the modules there. Thus, no need to pass your environment to the modules as well.
Basically the same concept as you did, but then in your AppModule. E.g.:
imports: [ BrowserModule, env.production ? MyProductionModule : MyDevModule ],
If you really really want to do that stuff in your library (I wouldn't recommend it myself), your approach would probably fit. But again, I personally wouldn't recommend it, based upon "seperation of concerns".
I was getting an error saying ngMetadataName was undefined when trying to inject the environment variable into a shared service, seemingly because injecting a provider into a service that's provided in root causes a circular dependency?
I can use Victor's excellent method without any trouble to inject into components.
To get the environment object into a shared service instance while avoiding the undefined error, now I'm using an app initializer provider with a factory.
app.initializers.ts
export const EnvironmentInitializer = {
provide: APP_INITIALIZER,
deps: [Injector],
multi: true,
useFactory: function initEnvironment(injector: Injector): () => Promise<any> {
return () => {
const initEnvironmentOn = [ ApiService ];
initEnvironmentOn.forEach(Class => injector.get(Class).environment = environment);
return new Promise<any>(resolve => resolve());
}
}
};
app.module.ts
import { EnvironmentInitializer } from './app.initializers';
providers: [
EnvironmentInitializer
],
api.service.ts (Still have to define the variable)
environment: any;
A solution that worked for me following @vsavkin comment
Create a folder named app-config
under libs and add an index.ts
file inside app-config folder. This lib can be shared across all your apps. Add the following content inside the index.ts file
import { InjectionToken } from '@angular/core';
export const APP_CONFIG = new InjectionToken('Application config');
Open base tsconfig.json
file and add the path for app-config so that it can be useful for importing into your app with @app-workspace/app-config
"paths": {
"@app-workspace/ui": ["libs/ui/src/index.ts"],
"@app-workspace/auth": ["libs/auth/src/index.ts"],
"@app-workspace/utils": ["libs/utils/src/index.ts"],
"@app-workspace/app-config": ["libs/app-config/index.ts"]
}
Now inside your apps open the file under apps/app1/src/app/app.module.ts
and make the following changes for the providers array
import { APP_CONFIG } from '@app-workspace/app-config';
import { environment } from '../environments/environment';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [
{ provide: APP_CONFIG, useValue: environment}
],
bootstrap: [AppComponent]
})
export class AppModule {}
Here is the sample environment.ts
file that is residing under app1
export const environment = {
production: false,
apiUrl: 'http://localhost:3000/api',
};
You can use app-config inside your shared libs as-well, for example lets say you are making api calls from within your shared lib libs/auth/src/lib/services/auth.service.ts
import { APP_CONFIG } from '@app-workspace/app-config';
import { Inject, Injectable } from '@angular/core';
@Injectable()
export class AuthService {
constructor(
@Inject(APP_CONFIG) private appConfig: any
) {
console.log(this.appConfig.apiUrl); // This will print `http://localhost:3000/api`
}
}
Hope this is helpful :) Also on a sidenote, you may have to restart your application if you get any error while using the imports.
The issue with this though, if I understand it correctly, is while this will be available to the components within your app, the environment data is available via being provided. This means it will not be available at the time the modules are being initialized, which means you can't use it to dynamically configure a module (which is typically done via TheModule.forRoot). I came up with a similar approach a while back and realized this solution didn't do the job I was looking for. I get around this by feeding the environment data to any module that needs to dynamically configure itself (such as altering its imports based on environment, which is extremely useful in CI/CD) by doing what I mention before.
If you're wondering "what reason would you ever want to alter the imports of a module", I use angular in-memory-api to provide HTTP interception for mock data. But I don't want that interception in production. You use the feature by importing the module, so I wanted a way to say, "if you're in production, don't import this module." Providers don't fit the bill here, they are available too late in the lifecycle.
Yes @sandeepsuvit, thank you, that is helpful, and the challenge I faced was that the environment path in the libs/app-config/index.ts isn't dynamic, so you can't point to this app's environment, and then that app's environment, because ..environments/environment just points to one specific environment, so it always points to the same environment.
Here's another approach to enable pointing to different environment files, more aligned with https://angular.io/guide/singleton-services#the-forroot-pattern than my previous attempts. I also acknowledge this is slightly off base from proper library, since I currently have a few .ts files in a flatter universal/shared/services/.ts, universal/shared/modules/.ts, etc. structure, so my apologies for extending the closed issue further. The typescript path aliases are neat too.
shared.module.ts
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ApiService } from '../services/api.service';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class SharedModule {
static forRoot(environment: any): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [
ApiService,
{ provide: 'environment', useValue: environment },
]
};
}
}
app.module.ts
import { InjectionToken, NgModule } from '@angular/core';
import { SharedModule } from '../../../../shared/modules/shared.module';
import { environment } from '../environments/environment';
@NgModule({
declarations: [],
imports: [
SharedModule.forRoot(environment)
],
bootstrap: [AppComponent]
})
export class AppModule { }
api.service.ts
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { catchError, shareReplay } from 'rxjs/operators';
import { of } from 'rxjs';
import { ServerResponse } from '../interfaces/server-response';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
body: {}
};
@Injectable()
export class ApiService {
constructor(
@Inject('environment') private environment,
private httpClient: HttpClient
) {}
get(url: string) {
const apiUrl = this.environment.urls.api + url;
return this.httpClient.get<ServerResponse>(apiUrl)
.pipe(catchError((error: HttpErrorResponse) => { return of(error); }), shareReplay());
}
}
ApiService can now be referenced from the shared folder by any app-specific component and it will use the correct environment for that app.
@vsavkin, thank you for your approach. It's worked for me once I solved one small typo.
provider: 'someEndpointUrl', useValue: config.someEndpointUrl
should be
provide: 'someEndpointUrl', useValue: config.someEndpointUrl
Would you be able to update your example, please? Thank you!
Here's my exact use case: I am factoring out Auth section (which uses Firebase) of one app into a shared library because I need to add more apps to the workspace which can make use of existing Auth section. The Problem: Official AngularFireModule expects Firebase config details in @ngModule using initializeApp() method and all of my apps need to use different Firebase config (api keys etc). My Approach: I am trying to pass config to AuthModule using forRoot() convention and then trying to use in-built APP_INITIALIZER provider inside the Auth lib. But, I have zero idea how to inject the config data inside AngularFireModule.InitializeApp().
Can Nx replace a plain text file inside a lib based on what app is trying to execute/ import it? Any help will be appreciated even if it's beyond the scope of Angular or Nx.
I solved the issue by using static property in the service (in shared lib) which was receiving config data. APP_INITIALIZER is the solution!
EDIT:
I solved the issue by using static property in the service (in shared lib) which was receiving config data. APP_INITIALIZER is the solution!
I have just has the same problem with this, could you share the code, pls?
EDIT:
I solved the issue by using static property in the service (in shared lib) which was receiving config data. APP_INITIALIZER is the solution!
I have just has the same problem with this, could you share the code, pls?
firebase.module.ts file:
import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { ModuleConfig } from './module-config.model';
import { FirebaseConfigService } from './firebase.config';
import { FirebaseService } from './firebase.service';
@NgModule({
imports: [
CommonModule,
AngularFireModule.initializeApp(FirebaseConfigService.data),
AngularFireAuthModule
],
providers: [FirebaseService]
})
export class FirebaseServiceModule {
static config(data: ModuleConfig): ModuleWithProviders {
return {
ngModule: FirebaseServiceModule,
providers: [
FirebaseConfigService,
{
provide: APP_INITIALIZER,
multi: true,
useFactory: (firebaseConfig: FirebaseConfigService) => () => {
firebaseConfig.setFirebaseConfig(data);
},
deps: [FirebaseConfigService]
}
]
};
}
}
firebase.config.ts file:
import { Injectable, Inject } from '@angular/core';
import { ModuleConfig } from './module-config.model';
@Injectable()
export class FirebaseConfigService {
static data = {
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: ''
};
setFirebaseConfig(moduleConfig: ModuleConfig) {
return new Promise<void>((resolve, reject) => {
FirebaseConfigService.data.apiKey = moduleConfig.firebaseAPIKey;
// Do this for all fields..
resolve();
});
}
}
module-config.model.ts file:
export interface ModuleConfig {
firebaseAPIKey: string;
// Put all fields which you want to transfer to the module
}
Now, to import this module into main module, use this:
FirebaseServiceModule.config({
// Pass configuration in adherence with ModuleConfig interface
})
Thank you for quick response! this is definitely help.
@SachinShekhar, @chungminhtu. Have you tried it with angular 8? When I try to test your solution I am not able to override the value of any FirebaseConfigService.data properties. It never changes from the default. Does this approach works for passing configuration to the angular library or only to angular feature (module)?
@Melmoth-the-Wanderer Yes, I use it with Angular 8 and you can pass the configuration data to a library (which is basically a feature module with special path configured with TS compiler).
You must be making some mistakes.
@SachinShekhar, I must be doing something wrong as well. You did not post your ./firebase.service file so I must be doing something wrong in the one I created.
I'm getting:
ERROR Error: Uncaught (in promise): Error: Your API key is invalid, please check you have copied it correctly.
NOTE: I have verified that the key I'm passing in the config is indeed correct.
The minute I try to inject any Firebase module into my FirebaseService constructor I get the invalid apiKey error so the config does not seem to be getting set OR my service file is just not set up properly.
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import {
AngularFirestore,
AngularFirestoreDocument
} from '@angular/fire/firestore';
import { of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { User } from '@nx-on-fire-io/shared/auth';
@Injectable({
providedIn: 'root'
})
export class FirebaseService {
constructor(
private afAuth: AngularFireAuth,
private afs: AngularFirestore,
) {
}
user() {
return this.afAuth.authState.pipe(
switchMap(user => {
if (user) {
return this.afs.doc<User>(`users/${user.uid}`).valueChanges();
} else {
return of(null);
}
})
);
}
}
@JefferE This setup is independent of firebase.service file.
Try debugging by console logging the configuration in a component first. Make sure that your firebase configuration object exactly matches the one provided by firebase (if you are passing few fields like apiKey only, that can be the issue).
@SachinShekhar Yes, my config is correct, it works in a project that is not trying to use my shared auth lib.
However, I now have this working but I'm not understanding why. My files are exactly the same as yours above and below is my main app module.
My app and shared auth lib ONLY works if I ALSO configure AngularFireModule.initializeApp here in my main app module in addition to doing it in my FirebaseServiceModule with the passed config.
import { FirebaseServiceModule } from '@nx-on-fire-io/shared/auth';
import { SharedAuthModule } from '@nx-on-fire-io/shared/auth';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import { UiMaterialModule } from '@nx-on-fire-io/ui-material';
import { AppRoutingModule } from './app-routing.module';
import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
UiMaterialModule,
AppRoutingModule,
SharedAuthModule,
BrowserAnimationsModule,
FirebaseServiceModule.config(environment.firebase),
AngularFireModule.initializeApp(environment.firebase),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
@JefferE What's the output of console.log()?
BTW, double initialization is bad thing which can cause unexpected behavior. Don't do this.
Thanks for your help with this!
If I don't do the double initialization (i.e. I only call AngularFireModule.initializeApp in FirebaseServiceModule),
I get this the minute the component that uses the shared service loads:
ERROR Error: Uncaught (in promise): Error: Your API key is invalid, please check you have copied it correctly.
at resolvePromise (:4200/polyfills.js:4032)
at resolvePromise (:4200/polyfills.js:3989)
at :4200/polyfills.js:4093
at ZoneDelegate.invokeTask (:4200/polyfills.js:3626)
at Object.onInvokeTask (:4200/vendor.js:99230)
at ZoneDelegate.invokeTask (:4200/polyfills.js:3625)
at Zone.runTask (:4200/polyfills.js:3403)
at drainMicroTaskQueue (:4200/polyfills.js:3794)
@SachinShekhar, I also forgot to mention that if I step through the code I can see that the config is indeed passed into the shared lib and my values seem to be getting set into the static var as expected,
@SachinShekhar
This is where the crash occurs. The minute this constructor runs:
export class FirebaseService {
constructor(
private afAuth: AngularFireAuth,
private afs: AngularFirestore,
) {
}
If I comment out AngularFireAuth and only inject AngularFirestore then it complains that appId must be a string (just another way of saying it doesn't like my config).
@JefferE Show me your module-config.model.ts and firebase.config.ts files if you followed my naming convention (or, show equivalent files).
in my lib module
CoreModule
I need to accessenvironment
which part of my appmyapp
libs/core/src/core.module.ts
TSLint is complinting this kind of inport.
looking for guideline how we can share app's
environment
with lib module.