Angular Service singleton constructor called multiple times
Asked Answered
S

2

24

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() {

  }

}
Samhita answered 24/7, 2018 at 15:43 Comment(0)
M
41

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.

Mungo answered 24/7, 2018 at 15:52 Comment(6)
Just some additional reference for the OP. What @Mungo is talking about is referred to as Provider Scope and there is a nice clear example discussing it in the Official Angular Docs: here on Provider ScopeJowers
@Mungo hi, I have removed TestService from CoreModule providers but still the same issue. This is the console output: imgur.com/a/s7JuLlU. The constructor seems to be called from once from .ts and once from .js. What does it mean?Samhita
@Samhita can you please your component code where you are using this service.Mungo
@Mungo please see edit, i have removed third-party codes for brevitySamhita
@Mungo i think I have found the problem. In my Dashboard component, i have this line import { TestService } from '../test.service.js'; ... my mistake. Thank you so much for your help and sorry for wasting your time!Samhita
I just had to comment...I looked ALL OVER the internet for a solution as to why I was having both a ts and js file being called and the singleton service not remaining in my code. After reading @Mungo 's post, searched my code and found a reference to a .js file. So many angry hours spent....anyway thanks for the clue!Zoroastrianism
A
1

@Injectable({ providedIn: 'root' })

  • The providedIn with root option means, the service is available throughout the application as singleton service.
  • It doesn't complain any error like No provider for xxxservice bcz of providedIn if you didn't added to providers list of any module.
  • If you do adding to the list will leads to creating a new instance of service, which will give you wrong results than expected.

NOTE : Please make sure to use the concept for better results.

Adenitis answered 4/6, 2020 at 11:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.