I've been trying to find a way where I can instantiate a service that will purely sit "in the background" and listen to events (and do stuff) - which I would like to be created when the app initializes, and be forgotten about.
Unfortunately, I would need to use dependency injection into a component, for the service to be instantiated - most paths I take lead to using the AppComponent
's constructor.
I won't be directly interacting with the service though (calling methods/properties), and want to keep it out of other components/services which don't have anything directly to do with it.
The service and the logic in it is pretty straightforward. My service is based on a Dynamic page titles in Angular 2 tutorial.
The service will listen to NavigationEnd
events from the Router
, grab the ActivatedRoute
, and then use the route's data to set the page title.
Unlike the example in the tutorial, I've created my own service instead of putting the logic within the AppComponent
; I want to keep my separation of concerns tip-top.
page-title.service.ts:
import { Injectable } from '@angular/core';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { filter, map, mergeMap } from 'rxjs/operators';
@Injectable()
export class PageTitleService {
constructor(
router: Router,
activatedRoute: ActivatedRoute,
titleService: Title
) {
router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
map(() => activatedRoute),
map((route) => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}),
filter((route) => route.outlet === 'primary'),
mergeMap((route) => route.data)
)
.subscribe((routeData) => titleService.setTitle(routeData['title']));
}
}
Obviously, the service itself will rely on dependency injection to use the Router
, ActivatedRoute
, and Title
services.
The only way I currently know to instantiate this service is to use dependency injection into another component/service.
E.g. in app.component.ts
:
export class AppComponent implements OnInit {
constructor(
pageTitleService: PageTitleService, // inject the service to instantiate it
// ... other services
) { }
ngOnInit() {
// do stuff with other services, but not the pageTitleService
}
}
The problem is, I want to avoid doing this if at all possible.
Is it possible to instantiate the service somewhere other than a component/service?
I do have an app-load.module.ts
, which does some upfront initialization, before the rest of the app is loaded:
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { OrganisationService } from './core/organisation/organisation.service';
export function initApp(organisationService: OrganisationService) {
return () =>
organisationService
.initialize()
.then(() => window.document.documentElement.classList.remove('app-loading'));
}
@NgModule({
imports: [],
declarations: [],
providers: [
OrganisationService,
{ provide: APP_INITIALIZER, useFactory: initApp, deps: [OrganisationService], multi: true }
]
})
export class AppLoadModule { }
Could I perhaps instantiate the PageTitleService
in here, somewhere?
Or, is there a better place/way to do it?
Thanks in advance.
If you want to use angular services (and Http is an angular service) you must inject them as I told above as a constructor attribute to another service / component , which means if you want to use Http you need to have your service injectable. So the answer is no, you can't do it in a nice way.
There are two ways to make a service a singleton in Angular: Set the providedIn property of the @Injectable() to "root" Include the service in the AppModule or in a module that is only imported by the AppModule.
It works. We have successfully created an Angular service without the @Injectable decorator.
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. Hence Angular service can be a singleton as well as non-singleton in nature.
Just an observation why injecting in a component (App component) would not be such a bad idea:
init
method in the service which will be called from app component so that it can start listening to router event and update the title. This makes it very imperative, explicit too and you can move calling this init method to some other component if required.Solution
Instead of injecting the service via your app.component.ts
, you could inject the service via the app.module.ts
(or via your app-load.module.ts
)
Eg:
@NgModule({
...
})
export class AppModule {
constructor(pageTitleService: PageTitleService) {}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With