Service Providers

Understand the changes to defining service providers

For most Angular applications, services can be provided very simply, simply by including the class in the array of providers in your module. With NgRX Auto-Entity, due to its dynamic nature we must change how services for entities are provided a little bit.

Mapping Model to Service

Instead of registering the service itself directly, we must instead register the model class as the provider, and map it to an entity service via the useClass option. Like so:

src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgrxAutoEntityModule } from '@briebug/ngrx-auto-entity';

import { AppComponent } from './app.component';

import { Customer } from 'models';
import { CustomerService } from 'services/customer.service';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, NgrxAutoEntityModule],
  providers: [
    { provide: Customer, useClass: CustomerService }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

When dispatching an action, the model class is specified as the first parameter, and as such the model class is the only thing NgRX Auto-Entity can use to look up the necessary service provider. By mapping the model class to the service class, you are leveraging a standard Angular paradigm to support dynamic service lookup at runtime.

Efficiency with Service Mappings

Auto-Entity makes it possible to reuse a single smart entity service with multiple entities. This approach allows another reduction in developer effort, requiring an initial effort up front to implement the entity service, however over the lifetime of the application that single service may be used countless times for countless entities.

Mapping each entity to a service may not be the most efficient approach. Each provider will be a new instance of the entity service. In order to supply a single service instance you can use a factory with another provider.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgrxAutoEntityModule } from '@briebug/ngrx-auto-entity';

import { AppComponent } from './app.component';

import { Customer } from 'models';
import { Product } from 'models';
import { Order } from 'models';
import { LineItem } from 'models';
import { EntityService } from 'services/entity.service';

export function provideEntityService(service: EntityService) {
  return service;
}

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, NgrxAutoEntityModule],
  providers: [
    EntityService,
    { provide: Customer, useFactory: provideEntityService, deps: [EntityService] },
    { provide: Product, useFactory: provideEntityService, deps: [EntityService] },
    { provide: Order, useFactory: provideEntityService, deps: [EntityService] },
    { provide: LineItem, useFactory: provideEntityService, deps: [EntityService] },    
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Minification and Service Lookup

To ensure that auto-entity's service lookup for a given identifier of application state will work even when your code has been minified/uglified, run through AoT, optimized, etc. we require that a string name for each model be defined with the Entity decorator. This name is used instead of the actual runtime class name, thus guaranteeing that even if the class name (and therefor constructor.name) is changed during minification, auto-entity will still function properly.

Last updated