1

I have an Angular application with module-per-feature architecture. I have an OrdersModule with components like ListOfOrders, DetailOfOrder or EditOfOrder

All of these components require OrdersApiService in constructor, which is basically a simple service that provides CRUD operations over HTTPS using REST API backend.

The OrdersModule works with an entity called Order

I had received a feature request where I will implement new entity, called a Draft Order. Practically there is no difference between Draft Order and Order (they contain the very same properties, they are created in the very same way) but they have to be stored differently, they have different API endpoint (myApi.com/orders vs myApi.com/draft-orders)

The easiest way (from time spent perspective) is to copy/paste Orders to new module. However, I would love to avoid code duplication, so this approach should work for me:

{
    path: 'orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)
},

{
    path: 'draft-orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)
},

Now I would need to inject different service to components according to routing. I know about the workaround

{
    path: 'orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule),
    data: {
      type: 'orders'
    }
},

{
    path: 'draft-orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule),
    data: {
      type: 'draft-orders'
    }
},

and in service consume ActivatedRoute:

constructor(
    private route: ActivatedRoute,
  ) {
    this.type = this.route.snapshot.data['type'];
  }

   getOrders() {
     return this.http.get('myApi.com/' + this.type);
   }

Is there any a solution how to inject whole service?

Thanks!

Luke1988
  • 1,850
  • 2
  • 24
  • 42

1 Answers1

0

First solution

If you want to go hard way, you can solve problem using useFactory method.

Implement api service and store api endpoint into readonly field of the service. Use useFactory method to inject all needed services along with baseUrl prop.

@Injectable()
export class ApiService {
  constructor(
    private logger: LoggerService,
    private userService: UserService,
    private readonly baseUrl: string
  ) {}

  getUrl() {
    return this.baseUrl;
  }
}

After that you will need to create provider factory, where you will decide which url to use based on some information (I used current route in this one)

@NgModule({
  imports: [RouterModule.forChild([{ path: "", component: OrderComponent }])],
  providers: [
    {
      provide: ApiService,
      // List of needed dependecies
      deps: [LoggerService, UserService, Router],
      // Dependencies order should be preserved
      useFactory: (
        logger: LoggerService,
        userService: UserService,
        router: Router
      ) => {
        const isDraft = router.url.includes("draft");

        return new ApiService(
          logger,
          userService,
          isDraft ? OrdersUrl.DRAFT : OrdersUrl.COMMON // inject ORDERS_URL
        );
      }
    }
  ],
  declarations: [OrderComponent]
})
export class OrderModule {}

This way you will have no duplicate code at all.

Codesandox

Second solution

Easier one will be just to decide what url to use right in the api service. For example, based on current route.

Temoncher
  • 644
  • 5
  • 15
  • thanks man! This gave me a direction. I am currently working on kind of splitting of modules. I will write an update to my original post later. Thanks! – Luke1988 Nov 20 '20 at 17:07