I was under the impression that a Angular Service
is a Singleton
, but have recently discovered, or atleast it seems, that the service is only a singleton to the instance of the component and the component's children.
Consider the following code:
const CACHE_REFRESH_INTERVAL = 1800000; //30 minutes
const CACHE_SIZE = 1;
const COMPANY_CACHE_KEY = 'company_cache_key';
@Injectable({
providedIn: "root"
})
export class CompanyService {
private companyCache: Map<string, Observable<any>> = new Map();
constructor(private httpClient: HttpClient) { }
public getAllCompanies(): Observable<CompanyViewModel[]> {
if (!this.companyCache[COMPANY_CACHE_KEY]) {
const timer$ = timer(0, CACHE_REFRESH_INTERVAL);
this.companyCache[COMPANY_CACHE_KEY] = timer$.pipe(
switchMap(_ => this.getAllCompaniesHttpRequest()),
shareReplay(CACHE_SIZE),
refCount(),
catchError(err => this.companyCache[COMPANY_CACHE_KEY] = undefined) //prevents storing error in cache
);
}
return this.companyCache[COMPANY_CACHE_KEY];
}
private getAllCompaniesHttpRequest(): Observable<CompanyViewModel[]> {
return this.httpClient.get<CompanyViewModel[]>(environment.endpoints.company.getAllCompanies());
}
}
Let's say, ComponentA
gets this service injected through the component's instructor. When ComponentA
calls the CompanyService.getAllCompanies()
method, the results will be stored in companyCache
. Now the user navigates to a new URL, ComponentA
get destroyed, as well as the instance of the service. Navigate back to ComponentA
, and a new instance of the service is injected, and companyCache
is once again empty.
Is my assumption correct? Can I make a service a singleton accross the entire application?
Services are a singleton at the level of the app.
Take this simple stackblitz demo
I have a trivial service that sets a random number from the constructor.
@Injectable({
providedIn: 'root'
})
export class Service {
constructor() {
this.rnd = Math.random();
}
private rnd: number;
getRand(): number {
return this.rnd;
}
}
I have a component structure like this with the service being injected into 2 "child" components (not actual children, but that's the pattern of navigation I have set up):
|- home
|- component a
|-- inject service
|- component b
|-- inject service
When navigating from home to component a, it will display the random number that was set in the constructor of the service.
When I navigate back to home (check the console to see that component a was destroyed), and then navigate to component b, it will display the random number that was set in the constructor of the service.
For the lifetime of the app, the random number remains the same. To me this proves that even when a service isn't "in scope", it is still in memory.
Not to mention that the docs talk about this on the very first line:
A singleton service is a service for which only one instance exists in an app.