Da tempo siamo abituati a creare nelle nostre applicazioni Angular un modulo specifico: CoreModule, ovvero il modulo che “sgrassa” AppModule consentendoci una maggiore pulizia. Ma ha ancora senso usarlo, ora che da un po’ di tempo esiste providedIn? Facciamo quattro chiacchiere.

Cosa risolve providedIn: ‘root’

Facciamo un breve recap: questa opzione serve ad indicare ad Angular che il servizio in questione è da considerarsi singleton a livello di applicazione (quindi istanziato una sola volta), e verrà istanziato non appena il servizio verrà richiesto da qualcuno.

@Injectable({
  providedIn: 'root'
})
export class MyService {}

Questa soluzione risolve un problema noto: il treeshaking dei servizi. In questo modo infatti, se nessuno richiederà MyService, quest’ultimo non verrà incluso nell’applicazione che di conseguenza sarà meno pesante.

Ma ne abbiamo sempre bisogno? E CoreModule, continuiamo ad usarlo?

Il servizio con dipendenze

Vediamo un caso comune: immaginiamo di avere un modulo CacheModule che si occupi di gestire la cache delle nostre chiamate http. Supponiamo che questo modulo abbia un servizio helper, CacheService, che ci permetta di interagire con la nostra cache:

@Injectable()
export class CacheService {

  constructor(@Inject(CacheConfigToken) private config: CacheConfig) {}

  add(...) {}

  remove(...) {}
}

Vediamo che il nostro servizio ha una dipendenza: la configurazione della cache. Questo perché, magari, abbiamo deciso di rendere il nostro modulo più intelligente e configurabile dall’esterno, in questo modo:

@NgModule()
export class CacheModule {

  static forRoot(config: CacheConfig): NgModuleWithProviders {
    return {
      ngModule: CacheModule,
      providers: [
        CacheService,
        { provide: CacheConfigToken, useValue: config }
      ]
    }
  }
}

Questo è solo un esempio (la dipendenza potrebbe essere, banalmente, un altro servizio), ma se noi in questo caso introducessimo providedIn: ‘root’ nel servizio, l’applicazione potrebbe rompersi se non fosse stato importato CacheModule, perché mancherebbe la configurazione.

L’alternativa a ‘root’

C’è un’altra strada; al posto di root, possiamo dire ad Angular di associare quel servizio all’Injector di un altro modulo, nel nostro caso, CacheModule.

import { CacheModule } from '../cache.module';

@Injectable({
  providedIn: CacheModule
})
export class CacheService {}

In questo caso è come se avessimo messo il servizio fra i providers del modulo, ma con una eccezione: avviene comunque il treeshaking, quindi se nessuno lo usa, il servizio non c’è.

Bisogna però stare molto attenti ad alcune cose:

  • Attenzione a non generare dipendenze circolari con quell’import del modulo
  • In questo modo non è più possibile sfruttare una delle più grandi feature di Angular, ovvero l’override di CacheService con un altro servizio!

In poche parole, questa sintassi ha comunque i suoi vantaggi:

providers: [ CacheService ];

// In caso di refactoring, potete fare questo:
providers: [ { provide: CacheService, useClass: OtherService } ]

// O questo
providers: [ { provide: CacheService, useExisting: OtherService } ]

// O questo
providers: [ { provide: CacheService, useValue: OtherService } ]

// O questo
providers: [ { provide: CacheService, useFactory: (...) => {...}, deps: [...] ]

Quindi, per quanto mi riguarda, in alcuni casi continuerò a inserire un servizio fra i provider perché mi dà maggior sicurezza. Il servizio appartiene a quel modulo, e se sono sicuro che quel servizio sia utilizzato dalla mia app, mi sta bene non approfittare del treeshaking.

CoreModule, che fine fa?

Questo è ovviamente un parere personale, ma si tratta di “come strutturare un progetto” e quindi non c’è scampo, ognuno farà come crede. Io continuerò a:

  • Usare CoreModule per raggruppare servizi singleton globali in una cartella “services“, anche se sono providedIn: ‘root’.
  • Usare CoreModule per raggruppare i moduli che servono all’intera app (Auth, User, Cache, ecc…), che a loro volta conterranno servizi. Altri potrebbero spostarli sotto /app, ma a me piace averli tutti in un unico posto.
  • Non usare CoreModule per nulla che non sia moduli o servizi.

Conclusione

Non c’è un giusto o sbagliato: basta scrivere codice ordinato e pulito, e avere chiaro come funzionano moduli e servizi in Angular per non incappare in errori. Infatti, con providedIn possiamo risolvere anche il problema dei moduli lazy che importano moduli shared con servizi… Ma questo sarà un altro articolo! 😉

Ti è piaciuto l’articolo? Condividilo!

Rilasciare risorse gratuite non è facile: un sacco di tempo vola fra la ricerca dell’idea, la stesura, la preparazione di codice funzionante e la revisione. Se ti piace ciò che facciamo, condividi l’articolo: nel peggiore dei casi ti daranno del secchione!

Ti interesserà anche…

Angular

Creazione di Guardie per i Moduli

I moduli sono una delle feature che più amo di Angular: ci permettono di racchiudere componenti, servizi, pipe e direttive all’interno di un unico contenitore. Ma non si tratta soltanto…
Angular

NgRx: cosa cambia nella versione 8

L’ottava versione di NgRx ha portato molti cambiamenti interessanti che ci consentono di scrivere codice più pulito e più snello. E c’è anche qualche chicca che vale la pena di…

Iscriviti alla nostra newsletter!

Rimani aggiornato per quando rilasceremo nuovi articoli, nuovi corsinuove promozioni e nuove risorse gratuite. Ti promettiamo che useremo la tua email con prudenza, e non la condivideremo con nessuno senza il tuo consenso!

Abbiamo a cuore la tua privacy. Niente spam. Leggi la nostra privacy policy qui.

Menu