2

We're building a cms driven application framework with Angular. We deliver multiple packages that can be used by customers to further customise the ui or logic. Components to that reason will delegate their logic to a service, with the intend customers can provide a custom service (we're avoiding component inheritance).

The components are dynamically being placed and are initialised with unique data set, holding the CMS data. The provided services will inherit this data from the component injector. This works great, however, when a custom service is provided (using InjectionToken), we face 2 problems:

  1. The service is no longer a non-singleton, multiple component instances will be provided with the same custom service
  2. The service will no longer inherit the data value from the component as it's injected in a different scope

Anyone else got into a similar architectural challenge?

tobi-or-not-tobi
  • 1,250
  • 1
  • 8
  • 10
  • 1
    @Component decorators have a `provider` property : providing the service from there makes a new instance of it at every new component instance. Did you try that ? –  Oct 16 '18 at 07:48
  • That's exactly what we're doing. We need however to be able to override the standard service by a custom one. This is achievable with a factory, however they are provided in a different injector scope, which is causing the 2 problems I've described. – tobi-or-not-tobi Oct 16 '18 at 07:53
  • Well if the services are only needed in the related components, why would you make them injectable ? Simply create a class, and create a new instance in your service. This way, you get rid of injection tokens, providers, dependency injection, and all of that Angular context you don't seem to need. –  Oct 16 '18 at 08:05
  • Please note: "with the intend customers can provide a custom service". We publish components in a library on npmjs, customers need to provide their own logic. – tobi-or-not-tobi Oct 16 '18 at 08:28

1 Answers1

0

Looks like what you want is then not a Provider directly. Seems what you want is a factory, which would be the actual provider, and then it will have a create method that will return you an instance of the actual service which is not a singleton.

something like

@Injectable()
export class MyPFactory {
  public create(): MyP {
    return new MyP();
  }
}

In this way you achieve to have a reusable service without going mental with the overriding and probably a much cleaner unit test. I wouldn't say this is strictly a solution is just a way that I used many times to approach your problem.

In this way MyP doesn't need to be decorated as Injectable since the factory will create it by calling the create method into the components/directive that are going to use it.

https://en.wikipedia.org/wiki/Factory_method_pattern

Valex
  • 1,149
  • 1
  • 8
  • 14
  • Thanks, I've indeed start experimenting with this. 2 reasons I don't like it: 1. It requires a custom configuration, similar to a InjectionToken 2. Customers might introduce their own dependencies, which we need to bring in when we create the service in our factory Agreed? Or did you have something clever in mind? – tobi-or-not-tobi Oct 16 '18 at 09:03
  • Can't say for sure because I'm not familiar with how the architecture of what are you building is working, but if those dependency are already in the IoC container, the factory can receive them as a normal dependency and feed the service with what he needs? and I know it requires a lot of boilerplate, but if you think unit test, this will make you skip a lot of headaches. – Valex Oct 16 '18 at 10:50
  • If you have factory that gets a service from the IoC container, and feeds it with the component instance specific data, then the service cannot be reused, as it's a singleton to all components that will be initialised. – tobi-or-not-tobi Oct 16 '18 at 11:49
  • I see, I thought what you needed where singletons from the app, I didn't understood that also what is coming from customer should be in the scope of that component, my bad. – Valex Oct 16 '18 at 12:40