Avoid Multiple Instances of Service in Angular - javascript

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.

Related

How to properly inject provider in NestJS?

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);
}
}

NestJS - how to provide services of dynamic modules

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.

Angular4/5 dependency injection docs not working

https://angular.io/guide/architecture#services
I'm following the docs on angular.io to inject dependencies like services, etc. I did everything they said and when I try to run it, the console keeps telling me:
Uncaught ReferenceError: LedgerService is not defined
I am doing nothing crazy except creating a simple component with a service where both constructors have console.log commands (constructors in both the component and service). I've done everything Angular says to do in their 2 paragraphs that details this feature of Angular.
The component itself is being injected into the main app module (with the service being injected into the component) and both the component and service were created with the Angular CLI. So there isn't much I've even done at all minus trying to inject the service. So I'm not sure where it is going wrong but it is definitely not working and just shows a blank page (when it previously had basic content by default).
I created both units, tried to specify providers in both the app.module and the component.ts file and neither works and yields the same error--when Angular claims either could work. I've also specified it as a private service within the constructor of the component.ts file.
Everything I've seen relating to this is always for Angular 1 or 2. Neither of which are even remotely similar to Angular 4/5.
If you really want to see this code, fine but it's literally just framework and nothing else:
bookkeeper.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-bookkeeper',
templateUrl: './bookkeeper.component.html',
styleUrls: ['./bookkeeper.component.css'],
providers: [LedgerServiceService]
})
export class BookkeeperComponent implements OnInit {
constructor(private service: LedgerServiceService) { }
ngOnInit() {
console.log("Ledger component works!");
}
}
app.module.ts:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { InterfaceComponent } from './interface/interface.component';
import { BookkeeperComponent } from './bookkeeper/bookkeeper.component';
#NgModule({
declarations: [
AppComponent,
InterfaceComponent,
BookkeeperComponent
],
imports: [
BrowserModule
],
providers: [
LedgerServiceService
],
bootstrap: [AppComponent]
})
export class AppModule { }
ledger-service.service.ts:
import { Injectable } from '#angular/core';
#Injectable()
export class LedgerServiceService {
constructor() {
console.log("wtf");
}
}
LedgerService is actually called LedgerServiceService because I initially created LedgerService manually and then tried to use the AngularCLI to generate a service and named it LedgerService and it created a service called LedgerServiceService. Naming is not what is wrong. I only initially called it simply LedgerService because I figured it would be confusing.
Your examples are missing the import.
Anywhere we use a custom type, we also need to import that type.
For that reason, in both the module and component you will need to add:
import { LedgerServiceService } from './your-path-here'
You can see this in the examples they give on https://angular.io/guide/dependency-injection

Angular service instantiated twice?

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;
}
}

Javascript Angular using code from different .ts file

I'm working on an Angular4 project and I have created a filter on my app.component.ts which I'm currently using on my app.component.html.
<button (click)="filterBy('cars')">Cars</button>
This works fine in this file by I want to use this same filter on my nav.component.html but I'd like to avoid recreating the filter code again on the nav.component.ts.
Is there a way of using this filter on my nav.component.html from app.component.ts ?
Migrate your created filter code in some new .ts file. Import this file in your app.component and other components by using import {ClassName} from file.ts syntax and then easily implement it in your component html code
You can create service, for example "filter.service.ts". Good thing in service is - it will create only one instance. So you use less memory, and app is faster.
Then - you can include it to the parent module:
import { FilterService } from "some/filter.service.ts";
#NgModule({
declarations: [ AppComponent, NavComponent],
providers: [ FilterService ]
})
export class MyModule { }
And use in your components like this:
import { FilterService } from "some/filter.service.ts";
constructor(filter: FilterService) {
this.filter = filter;
}
And then your html:
<button (click)="filter.filterBy('cars')">Cars</button>
#Injectable()
export class Service{
//create all the logic here for all the things you want to be shared across compponents
}
call this in the component you want to use like
import {Service} from "../shared/service";
#Component({
selector: 'app-angular4'
})
export class Angular4Component implements OnInit {
constructor(private service:Service) {
// resuse all the methods
}

Categories