20

What are the usages for useExisting provider?

Is it useExistingOrThrowIfThereIsNone or useExistingOrCreateIfThereIsNone? Can one of these behaviours be chosen on purpose somehow, depending on our needs? If one of them is not supported, can an unsupported one be emulated?

The documentation is totally unclear on that and just gives an example that useExisting can reuse an instance from useClass.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565

3 Answers3

44

With this example

providers: [
    A, 
    {provide: B, useClass: A}, 
    {provide: C, useExisting: A}]

If you have

constructor(private a: A)

an instance for the first provider is created.

constructor(private b: B)

an instance for the 2nd provider is created

constructor(private c: C)

the instance of the first provider is injected.

If you start fresh with

constructor(private c: C)

an instance for the first provider is created and injected

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • `useExisting` is like an alias for another provider. `useClass` is just a new provider that maps from the requested type to the actual type but the key is the requested type. – Günter Zöchbauer Jul 17 '16 at 17:15
  • You don't need to do anything to make `Service` a singleton everywhere except, just providing it once at the root component. Angular2's DI maintains a single instance per provider. If you provide it just once there will be only one instance. – Günter Zöchbauer Jul 17 '16 at 17:42
  • This is currently not supported. Also Angular2-provided stuff like `ROUTER_PROVIDERS` have to be added explicitly at `bootstrap()` or the root component. – Günter Zöchbauer Jul 17 '16 at 18:07
  • 1
    To avoid 2 different instances to be created when 2 different providers point to the same class. – Günter Zöchbauer Jun 11 '17 at 08:15
15

When we write {provide: A, useClass: B}, Angular will create map between token A and class B.

When we write {provide: A, useExisting: B}, Angular will create map between token A and token B.

Difference between these maps:

  • token A -> instance of class B
  • token A -> token B -> instance of some class for token B
ktretyak
  • 27,251
  • 11
  • 40
  • 63
15

Just small addition/clarification to @GünterZöchbauer's answer.

It's actually useExistingOrThrowIfThereIsNone when we're talking about tokens. useExisting creates an alias to another token, not an instance, so there must be the token referred by useExisting, otherwise exception will be thrown. But when we're talking about instances, it will work as long as last token in the chain registers instance, so in that sense it is useExistingOrCreateIfThereIsNone.

Consider this:

// T = token, I = instance
providers: [
    {provide: B, useClass: A}, // T[B] => I[A]
    {provide: C, useExisting: A}] // T[C] => ??? - there's no T[A] declared

...
constructor(private a: B) {} // works
...
constructor(private a: C) {} // throws an exception: 

In this case second declaration will throw an error because token C refers to token A but there is no token A declared anywhere, even though there's an instance of class A in the injector. Angular will not attempt to create instance A for token C or associate token C with existing instance A. I just accidentally verified it in one of my projects. :)

The following will work though because of reasons well described in other answers:

providers: [
    {provide: B, useClass: A}, // T[B] => I[A]
    {provide: C, useExisting: B}] // T[C] => T[B] => I[A]

...

constructor(private a: B) {} // works
...
constructor(private a: C) {} // works

In this example instance of A will be created for token C even if there was no instance A created previously for token B. So, for token C it is "use whatever instance should be provided for token B", and for token B it is "use existing instance A or create new if there's none".

Alexander Leonov
  • 4,694
  • 1
  • 17
  • 25
  • Great example, ran into the same problem scenario – Drenai Dec 18 '18 at 23:46
  • The last line is wrong! for token B it is "create a new instance of A" (no matter if there exists one) – Vahid May 26 '21 at 19:41
  • Nope. Last line is correct. Instances are reusable and being cached by angular by default. If that's not how it works in your case then something else is going on. Post your code in separate question and someone will answer it. – Alexander Leonov May 31 '21 at 13:07