isolated angular service instance within an ngModule

45 views Asked by At

I have a scenario where I want to share a service within a NgModule. but at the same time I want to keep its instances usage separate.

stackblitz

in the stackblitz, you can see that I have a hover-report\hover-report.module.ts. in this module I have two component and one service that is shared between them.

HoverReportComponent show hover box.

HoverSectionComponent show the number of times I hovered over the box.

HoverService keep the count of number of times I hovered over the box.

hover.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HoverReportComponent } from './hover-report.component';
import { HoverSectionComponent } from './hover-section.component';
import { HoverService } from './hover.service';



@NgModule({
  declarations: [
    HoverReportComponent,
    HoverSectionComponent
  ],
  imports: [
    CommonModule
  ], 
  providers:[
    {provide: HoverService, useClass: HoverService}
  ],
  exports:[
    HoverReportComponent,
    HoverSectionComponent
  ]
})
export class HoverReportModule { }

hover.service.ts

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

@Injectable()
export class HoverService {
  private counter = new BehaviorSubject(0);
  counter$ = this.counter.asObservable();

  addOne() {
    console.log('add one' + this.counter.value);

    this.counter.next(this.counter.value + 1);
  }
}

hover-report.component.ts

import { Component } from '@angular/core';
import { HoverService } from './hover.service';

@Component({
  selector: 'app-hover-report',
  template: ` 
    {{hoverService.counter$|async}}
  
  `,
  styles: [
    `
      :host {
        display: block;
      }
    `,
  ],
})
export class HoverReportComponent {
  
  constructor(public hoverService:HoverService) {

  }
  
}

hover-section.component.ts

import { Component } from '@angular/core';
import { HoverService } from './hover.service';

@Component({
  selector: 'app-hover-section',
  template: ` <div class="hover-me" (mouseover)="onHover($event)"></div> `,
  styles: [
    `
      :host {
        display: block;
      }

      .hover-me{
        width:300px;
        height:200px;
        border: 1px dashed black;
      }
    `,
  ],
})
export class HoverSectionComponent {
  constructor(private hoverService: HoverService) {}

  onHover($event: MouseEvent) {
    this.hoverService.addOne();
  }
}

then there are two separate modules named RedModule and BlueModule that each import HoverReportModule and use the exported components to render a different variation based on its need.

red.component.ts:

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

@Component({
  selector: 'app-red',
  template: `
    
    <app-hover-section></app-hover-section>
    <h2>
      <app-hover-report></app-hover-report>
    </h2>
  `,
  styles: [
    `
    :host{
      display:block;
      background-color: #f8ad9d;
      padding: 20px;
    }
    `
  ]
})
export class RedComponent {

}

blue.component.ts

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

@Component({
  selector: 'app-blue',
  template: `
    <h2>
      <app-hover-report></app-hover-report>
    </h2>
    <app-hover-section></app-hover-section>
  `,
  styles: [
    `
      :host {
        display: block;
        background-color: #8ecae6;
        padding: 20px;
      }
    `,
  ],
})
export class BlueComponent {}

the point is, to keep the render components separate so we can use it differently inside a page, based on the design, but at same time, they should behave as one.

my mental model about modules is that a provider is instantiated, at its module level. but apparently I'm missing something.

as you can see even I have two modules (blue and red) each time I hover on one of them, the other one also get updated

enter image description here

so my question is how do I create a service whereby, in a way it is isolated to its usage within RedModule and BlueModule but shared within HoverReportModule.

I don't want to redefine the provider in RedModule and BlueModule. I want to encapsulate it within HoverReportModule so each developer won't be bothered if they forget it

 providers:[
    {provide: HoverService, useClass: HoverService}
  ],

my next question would be ... is it convertible to standalone component? at angular standalone component's document suggests to export components that should render together as a const array

export const HoverReport=[ HoverReportComponent, HoverSectionComponent] as const;

but it doesn't talk about how to deal with such cases where the provider should be shared within

0

There are 0 answers