Pattern to enable customization through DI
Asked Answered
F

1

0

I'm looking at the implementation of in-memory-web-api and there is the following code:

@Injectable()
export class InMemoryBackendService {
  protected config: InMemoryBackendConfigArgs = new InMemoryBackendConfig();
            ^^^^^^
  ...

      constructor(
        @Inject(InMemoryBackendConfig) @Optional() config: InMemoryBackendConfigArgs 
                                                   ^^^^^^
        ) {
        ...

As I understand the pattern is the following:

  1. Defined class property and instantiate a dependency without using DI
  2. Optionally inject dependency

If a user provides modified dependency through DI, it will be injected and the default one instantiated without DI will be overridden. I suspect something similar maybe with RequestOptions in HTTP module.

Is this a common pattern?

EDIT:

It turns out that in-memory-web-api is not exactly the pattern I'm asking about. Suppose, I have a class A that uses instance of class B injectable with the token B. So they are both registered with the root injector:

providers: [A, B]

Now, if a user wants to customize B, he can register the customized version under the same token, thus effectively overrriding the original B:

providers: [{provide:B, useClass: extendedB}]`

This is how RequestOptions can be extended in http module.

Francis answered 20/2, 2017 at 14:57 Comment(0)
B
2

The default value isn't just overridden. The most important part here is

Object.assign(this.config, config || {})

Nothing would happen without it.

This pattern isn't specific to DI, it is a common recipe for default property values, similar to _.defaults.

I would say that InMemoryBackendConfig default implementation is useless abstraction here. Since this.config is always merged with config, the former could be just a plain object

  protected config: InMemoryBackendConfigArgs = { ... };

InMemoryBackendConfig and RequestOptions use complicated variations of this pattern. Yes, in most basic form this is how this can be done:

providers: [{provide:B, useClass: extendedB}]`

This pattern is widely used by constant services in AngularJS for configuration objects, but having B as a class instead of plain object allows to extend the original values instead of replacing them.

Bellows answered 20/2, 2017 at 15:48 Comment(4)
yeah, thanks, I missed that they don't provide access modifier for config in the constructor. So this is different pattern then. The example with RequestOptions of http module is more to the point. Let me edit the question.Francis
by the way, what's your linkedin account?Francis
Actually, I see RequestOptions as a more complicated version of this and quite unique. Instead of assign, It does more complex merging with merge method. It is a available as a service but used through another helper function..Bellows
I've updated the answer. Yes, the pattern you've suggested is supposed to work like that. I would personally stick to an injectable class for config objects. Angular built-ins aren't the best examples because they are instantiated directly with new as well, this doesn't make the things more simple.Bellows

© 2022 - 2024 — McMap. All rights reserved.