Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Service singleton constructor called multiple times

I am trying to use an app-wide service (UserService) that stores authenticated user details. I have set up some routes but found that UserService is instantiated per route. I want them to share the same UserService.

I have created a CoreModule containing TestService as provider and imported it into AppModule.

core.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TestService } from '../test.service';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [],
  providers: [
    TestService
  ]
})
export class CoreModule { }

test.service.ts:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class TestService {
  constructor() { console.log('testService constructor called');}
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AdminComponent } from './layout/admin/admin.component';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';


import { CoreModule } from './core/core.module';

@NgModule({
  declarations: [
    AppComponent,
    AdminComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    CoreModule
  ],
  providers: [
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app-routing.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { BasicLoginComponent } from './basic-login/basic-login.component';
import { HttpClientModule } from '@angular/common/http';
import { AdminComponent } from './layout/admin/admin.component';    

const routes: Routes = [
  {
    path: '',
    component: AdminComponent,
    children: [
      {
        path: 'home',
        loadChildren: './dashboard/dashboard.module#DashboardModule'
      },
      {
        path: 'user/profile',
        loadChildren: './user-profile/user-profile.module#UserProfileModule'
      }
    ]

  },
]
@NgModule({
  imports: [
    CommonModule,
    RouterModule.forRoot(routes),
    HttpClientModule
  ],
  exports: [
    [RouterModule]
  ],
  declarations: []
})
export class AppRoutingModule { }

I have injected the TestService into DashboardComponent and UserProfileComponent constructors. However, when routing between two of these components, the TestService constructor is called twice.

It seems so straightforward but somehow I can't get it right. Can anyone point me to the right direction to troubleshoot this?

*edit

dashboard.component.ts

import {AfterViewInit, Component, OnInit, ViewEncapsulation} from '@angular/core';
/*import {NotificationsService} from 'angular2-notifications';*/

import { UserService } from '../user.service.js';
import { LocalStorageService } from '../../../node_modules/ngx-webstorage';
import { TestService } from '../test.service.js';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class DashboardComponent implements OnInit, AfterViewInit {


  constructor(private userService:UserService, private localSt:LocalStorageService,
  private testService:TestService) { // private servicePNotify: NotificationsService
  }


  ngOnInit() {

  }

}

user-profile-component.ts:

import {Component, OnInit} from '@angular/core';
import {animate, style, transition, trigger} from '@angular/animations';
import {Http} from '@angular/http';
import { TestService } from '../test.service';

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: [
    './user-profile.component.scss',
    '../../assets/icon/icofont/css/icofont.scss'
  ],

})
export class UserProfileComponent implements OnInit {

  constructor(public http: Http, private userService: UserService,
  private testService:TestService) {
  }

  ngOnInit() {

  }

}
like image 854
daisura99 Avatar asked Jul 24 '18 15:07

daisura99


People also ask

How do you prevent to use a service as a singleton in Angular?

There are multiple ways to prevent this: Use the providedIn syntax instead of registering the service in the module. Separate your services into their own module. Define forRoot() and forChild() methods in the module.

Can we have multiple instances of a service in Angular?

The answer would be no. The main objective of angular services is to share data across Angular application. Practically an angular service can be shared between all the components or can be limited to some component.

How many instances of service get created in Angular?

Angular DI maintains a single instance per provider. In your case if you have two constructor parameters with the same type, they resolve to the same instance.

What is providedIn in Angular service?

The providedIn allow us to specify how Angular should provide the dependency in the service class itself instead of in the Angular Module. It also helps to make the service tree shakable i.e. remove the service from the final bundle if the app does not use it.


1 Answers

As you have declared the TestService as -

@Injectable({
  providedIn: 'root'
})

Which means you are adding to AppRoot module.

No need to add explicitly in the CoreModule, so remove from providers of CoreModule. Remove following -

providers: [
    TestService
  ]

As you are adding the TestSevice in CoreModule which is already added in RootModule that's the reason it constructor getting called multiple times.

So use either of one from above.

like image 142
eduPeeth Avatar answered Sep 30 '22 01:09

eduPeeth