Home > Mobile >  Using injectiontoken for module specific settings in angular
Using injectiontoken for module specific settings in angular

Time:08-05

I have an app with 2 main modules, web and admin.

Admin declares

  providers: [
    {
      provide: DISPLAY_LIFE_TOKEN,
      useValue: 6000,
    },
  ],

and web declares the same token with useValue: 10000.

Both modules contain a route and components that use a service with providedIn: 'root', that imports the token DISPLAY_LIFE_TOKEN.

Yet in the service, the config value is always 10000 even when calls come from the admin module.

See demo.

Did i misunderstand something about injectionTokens and providers, or is there another preferred way to set a module wide config?

CodePudding user response:

So unless the module is a lazy loaded module, the providers are not created as separate instances, that is the issue, I guess the latest value of DISPLAY_LIFE_TOKEN overrides the previous one.

One fix will be to use the providers array inside the root component of the module, this will ensure a separate instance is created for the provider DISPLAY_LIFE_TOKEN. Also messageService needs to have two separate instances! so remove the providedIn: 'root'

@Component({
  selector: 'app-admin-main',
  template: `admin: {{value}}`,
  providers: [
    MessageService,
    {
      provide: DISPLAY_LIFE_TOKEN,
      useValue: 6000,
      useExisting: true,
    },
  ],
})

export class AdminMainComponent implements OnInit {

stackblitz

The second fix will be to simply convert them to lazy loaded modules instead.

CodePudding user response:

Services are singleton in Angular.

The only way around this is lazy loading. This ensures a separate instance is created for services provided in the lazy module.

Remove providedIn: 'root', from the service. Add the service to both module providers array.

  providers: [
    MessageService,
    {
      provide: DISPLAY_LIFE_TOKEN,
      useValue: 6000,
    },
  ],

And update the router module to use lazy loading:

   {
      path: 'admin',
      loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
    },
    {
      path: 'web',
      loadChildren: () => import('./web/web.module').then(m => m.WebModule),
    }]

(since this moves the path from admin module to app module, do not forget to also update the route in the admin module).

Demo

  • Related