I create a Firebase provider like below(without using module file with exports and imports):
#Injectable()
export class FirebaseProvider {
public app: admin.app.App;
constructor() {
this.app = admin.initializeApp(config);
}
}
and add this provider to providers in AppModule:
providers: [AppService, FirestoreProvider, ClientService],
and in ClientModule:
providers: [CustomersService, FirestoreProvider],
so, now i want to use this provider in my ClientService, so I inject this provider via constructor like below:
constructor(
private readonly firestoreProvider: FirestoreProvider,
) {}
and now i have a problem, can somone tell me why NestJs inject me this provider more than one time and because of this firebase throw me this error:
The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple
apps, pass a second argument to initializeApp() to give each app a unique name.
I initialize this firebase only in this provider, so how to properly use this provider in services across the whole project?
thanks for any help!
I don't think you want to declare your FirestoreProvider in the provider:[] array of a module more than once. Try something like this:
#Module({
imports: [
FirestoreModule
],
})
export class AppModule{}
Now that you've imported the FirestoreModule, you can use it in any class that is in this AppModule. Example:
#Injectable()
export class FooService {
constructor(private firestore: FirestoreProvider) {}
}
The key here is to define your provider in its own module, then export it via the exports:[] array, and provide it via the providers:[] array of that module.
import { FirestoreProvider } from './FirestoreProvider'
#Module({
providers: [ FirestoreProvider ],
exports: [ FirestoreProvider ],
})
export class FirestoreModule{}
#Injectable()
export class FirestoreProvider {
public app: admin.app.App;
constructor() {
this.app = admin.initializeApp(config);
}
}
Related
I'm struggling to figure out on how to provide services from DynamicModule to regular Modules. Pseudocode below:
app.module.ts
#Global()
#Module({
imports: [
DynamicModule.forRoot(config),
RegularModule,
],
providers: [],
exports: [],
})
export class AppModule {}
dynamic.module.ts
#Module({})
export class DynamicModule implements OnModuleInit, OnModuleDestroy {
constructor(private dynamicService: dynamicService) {}
static forRoot(config: Config): DynamicModule {
return {
module: DynamicModule,
imports: [],
providers: [
{
provide: CONFIG_TOKEN,
useValue: config,
},
DynamicService,
],
exports: [
DynamicService,
],
};
}
}
dynamic.service.ts
#Injectable()
export class DynamicService {
constructor(
#Inject(CONFIG_TOKEN) private readonly config: Config,
) {}
}
regular.module.ts
#Module({
imports: [],
providers: [RegularService, DynamicService],
exports: [RegularService],
})
export class RegularModule {}
regular.service.ts
#Injectable()
export class RegularService {
constructor(
private readonly dynamicService: DynamicService
) {}
}
Providing DynamicService to RegularModule requires to provide CONFIG_TOKEN in RegularModule as well, which seems odd and not practical in case more modules would depend on DynamicService and doesn't seem to be the correct way.
What concepts am I missing and what is correct approach to use services of a DynamicModule?
Would something as forFeature in DynamicModule method would be the right direction?
dynamic modules are modules that need some sort of context defined input, classic example is a database module that would need at least the host url and credentials, which would vary by app.
This means that when the forRoot(input) returns, you have a module just like any regular (non dynamic) module. As such, you can make use of the config value inside the dynamic module's service, export the service on the dynamic module, and then inject that service on other modules that import your dynamic module.
There is no need to also inject the config value on the service that injected the dynamicService.
If you need to have direct access to the config value inside of regularService, and that value is being shared across multiple services and modules, then you should take a look at the ConfigModule and treat that config value as env. If by some very specific reason it cant or should not be in env, then still you should create a separate module for providing this config values.
I have done some research on the net, and I have figured out that the problem I am facing is that multiple instances of service are being created, and I want to avoid that. Can some one please look at my code and spot the change I need to make.
Second service that uses the primary service that is being duplicated.
export class SecondaryService {
constructor(private primarySvc: IPrimaryService){
this.primarySvc.someSubject.subscribe(() => {});
}
}
Primary Service (the one that is being duplicated)
export class PrimaryService {
someSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
constructor(){}
}
Primary Service Provider
#Injectable()
export class PrimaryServiceProvider extends PrimaryService {
constructor(){
super();
}
}
Secondary Service Provider
#Injectable()
export class SecondaryServiceProvider extends SecondaryService {
constructor(private PrimaryProvider: PrimaryServiceProvider){
super(PrimaryProvider);
}
}
app.module.ts
#NgModule({
declaration: [SecondaryComponent],
exports: [SecondaryComponent],
imports: [BrowserModule],
providers: [SecondaryServiceProvider, PrimaryServiceProvider ]
})
export class SearchModule{}
Now I am trying to use the component I made in a local environment which looks something like this:
app.module.ts
#NgModule({
declaration: [AppComponent, HomeComponent],
imports: [SearchModule, BrowserModule],
providers: [PrimaryServiceProvider, SecondaryServiceProvider],
bootstrap: [AppComponent]
})
export class AppModule{}
home.component.ts
export class HomeComponent {
constructor( primarySvc: PrimaryServiceProvider,
secondarySvc: SecondaryServiceProvider) {
this.primarySvc.someSubject.next(false);
}
}
Now I know for sure the Primary Service has two instances since someSubject is not in sync, and the subscribe in SecondarySvc is not fetching any values from home.component.ts
Please tell me where do i need to make the changes
Thanks!!
Answering my own Question:
The services were duplicated due to incorrect file path while importing. Windows lets you use case-insensitive file paths, which may create multiple instances for every time the module is used.
I think you are overengineering using the PrimaryServiceProvider and SecondaryServiceProvider services.
To create a singleton service just set providedIn: 'root' option in the Injectable decorator, like this:
#Injectable({ providedIn: 'root' })
export class SecondaryService {}
At last, be sure to not register a singleton service at any providers array.
Check out the official documentation about this here.
I want had a project that initialize firebase by using AngularFire. However, during the entire development I didn't use much function from AngularFire. I always import the firebase/app at every services and use the respective function like firebase.auth() or firebase.database().
With that experience, I would like to initialize the firebase without AngularFire since I am not using the AngularFire methods.
Now come with my problem is I can't find any source to teach me how to initialize the firebase, and of course I am seeing error during importing the firebase at app.module.ts.
Below is my code:
Install the firebase by using: npm i firebase
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import * as firebase from 'firebase';
import { environment } from '../environments/environment'; //API key is imported
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
firebase.initializeApp(environment.firebase) //here shows the error
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
At the line of firebase.initializeApp(environment.firebase), it shows the error:
Type 'App' is not assignable to type 'any[] | Type<any> |
ModuleWithProviders<any>'.
Type 'App' is not assignable to type 'ModuleWithProviders<any>'.
Property 'ngModule' is missing in type 'App'.
Thanks in advance.
I would say you are having this error because firebase.initializeApp(environment.firebase) is not Angular Module.
I would suggest that you do this initialisation in one of your services or create a service for that effect.
#Injectable({
providedIn: 'root'
})
export class FirebaseService {
constructor() {
firebase.initializeApp(environment.firebase)
}
}
Well, you cannot import an object which is not an Angular module and you get that error because of that.
You can:
1. Use it in the AppModule constructor like:
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(){
firebase.initializeApp(environment.firebase);
}
}
2. Create a dedicated service for Firebase in which you will import 'firebase' lib (which is a good overall solution which will be probably used in your application globally).
#Injectable({
providedIn: 'root' // <-- If you are on Angular 6
})
export class FbService {
constructor() {
firebase.initializeApp(environment.firebase);
}
}
Just add firebase.initializeApp(environment.firebase); outside the module and the class, i.e:
import { environment } from '../environments/environment';
import * as firebase from 'firebase';
firebase.initializeApp(environment.firebase);
#NgModule({....
If you want to use it inside a service import firebase import * as firebase from 'firebase'; and use it as usual.
somehow I've got a service that seems to be instantiated twice (its properties are not in sync), by doing the following:
#Component
export class MyComponent extends someOtherComponent {
constructor(service: Service, service2: Service2) {
super(service, service2);
}
isStateEqual() {
return this.service.serviceState === this.service2.service.serviceState;
}
}
#Injectable
export class Service {
serviceState = {}
}
#Injectable
export class Service2 {
constructor(service: Service) {}
}
This is just a very basic example, but that's what it comes down to. To be more precise: We're building our own datepicker and extending NgbDatepicker component which has KeyMapService (this uses NgbDatepickerService) and a local NgbDatepickerService.Here is a link to the component: https://ng-bootstrap.github.io/#/components/datepicker/examples
In our app isStateEqual will always return false (even right after initialising the component) while in the demo you can find in the link above it will always return true (which is how it should be).
Anyone knows why it could be like that?
Thanks in advance.
Regards
Dennis
Application wide singletons must be defined on the bootstrapped module:
platformBrowserDynamic()
.bootstrapModule(AppModule)
#NgModule({
providers: [SingletonService1, SingletonService2],
bootstrap: [AppComponent]
})
export class AppModule {}
source
or by setting providedIn: 'root' on the service decorator:
#Injectable({
providedIn: 'root',
})
export class UserService {
}
source
In my case the service was instantiated twice, because I imported the service using two different approaches (my IDE (VS2019) mishelped me here by automatically generating the incorrect import):
import { Service } from '#mycompany/mymodule'
and
import { Service } from '../../../dist/#mycompany/mymodule/#mycompany-mymodule';
Visual code import my service in this way automatically
import { ConfigService } from 'src/app/services/config.services.js';
And the correct way is:
import { ConfigService } from 'src/app/services/config.services';
It depends where you declare the provider for your services - this determines the scoping. You don't specify in your question where you've done this - try doing it in AppModule, for example.
"service" must be a public propertie of Service2.
#Injectable
export class Service2 {
service:Service
constructor(service: Service)
{
this.service=service;
}
}
Typescript 2.4 introduce [import()][1] method to load modules dynamically. I am trying to load components dynamically using the following mentioned procedure https://angular.io/guide/dynamic-component-loader where we need to mention entryComponents in the module.
I am able to load all component modules dynamically using import() method in the AppModule but I am not able to get entryComponents of the modules. For example,
I have a component and its module
data-widget.component:
#Component({
selector: 'data-widget',
templateUrl: 'data-widget.component.html'
})
export class DataWidget{}
and
data-widget.module.ts
#NgModule({
imports: [ ...,
declarations: [DataWidget],
exports: [DataWidget],
providers: [],
entryComponents: [DataWidget]
})
export class DataWidgetModule {
constructor(private widgetService: widgetService) {
widgetService.register('weather', DataWidget);
}
}
I am now able to load that module dynamically using import() method in the following way
app.module.ts
#NgModule({
imports: [ ...,
declarations: [],
exports: [],
providers: []
})
export class AppModule {
constructor(private apiService: apiService) {
apiService.getMoudleUrls().subscribe( data=>{
import(''+data); //e.g './data-widget/data-widget.module'
},
error=>{});
}
}
In AppModule module the DataWidgetModule loaded in Webpack dynamically.Now I am trying to load DataWidget Component dynamically. You can see that I have register the component in DataWidgetModule using widgetService. But this function was not called during module loading as well as the entryComponents are also not loaded. So, when I tried to load the component using the following way which I have mentioned above
this.componentFactoryResolver.resolveComponentFactory(widgetService.getRegisteredWidget());
then I am getting error as entryComponents are not loaded in #ngModule. How can I load the entryComponents and register the components for dynamic load? It will be very helpful to get a solution for it. Thank you.
I don't know if this is the right topic, but I had a similar problem and found a little hacky solution.
The problem was that I wanted to use the entry components of a new compiled module inside the parent module. The entry components of the loaded and compiled module are present only in the compiled module so I had to add them to the parent module entry components.
Just add the entry components factories to the parent component factories.
const resolver = moduleRef.componentFactoryResolver;
resolver._factories.forEach(function (value, key) {
resolver._parent._factories.set(key, value);
});
In my case I had only a few components and entry components in the loaded module so it is no overhead to merge them with the parent component factories. For larger loaded modules I think it would be good to only merge the entry components...
I find a solution for it. The following code will help you to solve this problem.
export class DynamicComponent {
injector: Injector;
compiler: Compiler;
#ViewChild('container', {read: ViewContainerRef})
container: ViewContainerRef;
componentRef: any;
constructor(private compFactoryResolver: ComponentFactoryResolver, injector: Injector,
private apiService: apiService) {
this.injector = ReflectiveInjector.resolveAndCreate(COMPILER_PROVIDERS, injector);
this.compiler = this.injector.get(Compiler);
}
addWidget(){
apiService.getMoudleUrls().subscribe( module_url=>{
let module_= import(module_url); //e.g './data-widget/data-widget.module'
module_.then(module_data =>{
const keys = Object.getOwnPropertyNames( module_data );
let moduleFactories = this.compiler.compileModuleAndAllComponentsSync(module_data[keys[1]]);
const moduleRef = moduleFactories.ngModuleFactory.create(this.injector);
const componentFactory = moduleFactories.componentFactories
.find(e => e.selector === 'data-widget'); // find the entry component using selector
this.componentRef = this.container.createComponent(componentFactory, null, moduleRef.injector);
const newItem: WidgetComponent = this.componentRef.instance;
},
error=>{});
}
}