I have created an Angular Component Library, which I distribute via NPM (over Nexus) to several similar projects. This contains a PageComponent
, which in turn contains a FooterComponent
and a NavbarComponent
. In NavbarComponent
exists a button, which triggers a logout
function. This function is to be provided via a PageService
of the respective project. For this purpose I created an AbstractPageService
in the Angular Component library (PageService
extends AbstractPageService
).
At first I solved this via the EventEmitter
. But since I had to provide a logout
function for each new page, I wanted to solve this via one service per project. I pass the PageService
(Project) with using the forRoot()
method of Angular Component Library.
Everything works as desired, but wanted to know if there is a better solution or if the solution is so recommendable at all?
I have the following solution for this:
Components Lib - components.module.ts
import {ModuleWithProviders, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
import {NavbarComponent} from './layout/navbar/navbar.component';
import {PageComponent} from './layout/page/page.component';
import {PageHeaderComponent} from './components/page-header/page-header.component';
// ... others ...
@NgModule({
imports: [
CommonModule,
RouterModule,
FontAwesomeModule
],
declarations: [
NavbarComponent,
PageComponent,
// ... others ...
],
exports: [
NavbarComponent,
PageComponent,
// ... others ...
]
})
export class ComponentsModule {
static forRoot(pageService): ModuleWithProviders {
return {
ngModule: ComponentsModule,
providers: [
{provide: 'PageService', useClass: pageService}
]
};
}
}
Component Lib - page.component.ts
import {Component, EventEmitter, HostBinding, Inject, Input, Output} from '@angular/core';
import {AbstractPageService} from '../../services/abstract-page.service';
@Component({
selector: 'dc-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.scss']
})
export class PageComponent {
@HostBinding('class') styleClass = 'd-flex flex-column';
@Input() customStyleClass = null;
@Input() showLogoutButton = true;
// @Output() logoutButtonClick: EventEmitter<any> = new EventEmitter();
constructor(@Inject('PageService') protected pageService: AbstractPageService) {
}
logout(): void {
this.pageService.logout();
}
}
Component Lib - abstract-page.service.ts
import {Injectable} from '@angular/core';
@Injectable()
export abstract class AbstractPageService {
abstract logout(): void;
}
And here the use in a project:
Project - app.module.ts
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {RouterModule, Routes} from '@angular/router';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
import {ComponentsModule} from 'components';
const appRoutes: Routes = [
{path: '', component: AppComponent},
// otherwise redirect to home
{path: '**', redirectTo: ''}
];
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
FontAwesomeModule,
RouterModule.forRoot(appRoutes),
ComponentsModule.forRoot(PageService),
],
providers: [
// {provide: 'PageService', useClass: PageService}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Project - page.service.ts
import {Injectable} from '@angular/core';
import {AbstractPageService} from '../../../projects/components/src/lib/services/abstract-page.service';
@Injectable({
providedIn: 'root'
})
export class PageService extends AbstractPageService {
constructor() {
super();
}
logout() {
console.log('Ausloggen!');
}
}
I used this approach but ran into one problem:
The PageService
is not the same singleton instance in the library as in the application. Because it is provided in multiple modules (application module and the library module), it creates a new instance for each module.
What I ended up doing was providing my service through a string
in the application module:
// app.module.ts in the root application
providers: [
{
provide: 'PageService',
useClass: PageService
}
]
Then injecting the service where needed using @Inject()
// anywhere in the root application where the service is needed
constructor(@Inject('PageService') private pageService: PageService) {}
Then, in the library, I created a simple interface:
export interface AbstractPageService {
logout(): void;
}
Then, I can simply inject the service in the library through the Inject()
decorator and type it using the interface (without the need to implement the interface in the root application):
// anywhere in the library where the service is needed
constructor(@Inject('PageService') private pageService: AbstractPageService) {}
Now both the root application and the library use the same singleton instance of the PageService
, as provided in the root application.
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