3

I have two injectable classes in my angular application

@Injectable()
class B {}

@Injectable()
class A {
  constructor(b:B) { }
}

I want class A to be Singleton and class B to be Transient

I came to know I can use ReflectiveInjector.resolveAndCreate in class A to get an instance of class B. Any better way of achieving this ?

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
Ravi
  • 2,470
  • 3
  • 26
  • 32
  • See also https://stackoverflow.com/questions/38472103/create-new-instance-of-class-that-has-dependencies-not-understanding-factory-pr/38473200#38473200 and https://stackoverflow.com/questions/38482357/angular2-how-to-use-multiple-instances-of-same-service/38483406#38483406 – Günter Zöchbauer Aug 30 '17 at 06:26

2 Answers2

5

Since all existing recipes for providers create singletons, even factory, you can create your own injector, inherit all providers from component injector and use resolveAndInstantiate method to get new instances every time:

import { Component, Inject, Injector, ReflectiveInjector } from '@angular/core';

class P {
}

const ps = [];

class C {
  constructor(@Inject(P) p) {
    ps.push(p);
  }
}

@Component({
  moduleId: module.id,
  selector: 'my-app',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  name = 'Angular';

  constructor(injector: Injector) {
    const parent = ReflectiveInjector.resolveAndCreate([P], injector);
    const child = parent.resolveAndCreateChild([C]);
    const c1 = child.resolveAndInstantiate(C);
    const c2 = child.resolveAndInstantiate(C);
    console.log(c1 === c2); // false

    console.log(ps[0] === ps[1]); // true

  }
}

Here is the demo.

Also bear in mind that ReflectiveInjector is deprecated in @5.x.x. And it seems that there's no alternative in the new StaticInjector. I reported an issue about that.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • Thanks for the answer Maxim. I have a question though , can't we use a regular factory method which returns a new instance of required object when needed ?Secondly isn't ReflectiveInjector deprecated ? Do you still recommend to use it – Ravi Aug 30 '17 at 05:36
  • @SRK, no, the factory doesn't return new instances every time, whatever it returns the first time it caches it. So even if you do `return new AClass()` inside you will still get a singleton – Max Koretskyi Aug 30 '17 at 05:38
  • @SRK, also, bear in mind that ReflectiveInjector is deprecated. And it seems that there's no alternative in the new StaticInjector. I [reported an issue](https://github.com/angular/angular/issues/18946) about that. You can read more about StaticInjector [here](https://blog.angularindepth.com/angular-introduces-staticinjector-should-you-care-4e059eca030c) – Max Koretskyi Aug 30 '17 at 05:39
  • Thank you . I was under the impression until now that a factory returns a new instance every time I call it . – Ravi Aug 30 '17 at 05:54
3

There is a way to solve this using StaticInjector and functional Javascript. Using Max Koretskyi answer, with few modifications I come up with this:

import { Component, Inject, Injector } from '@angular/core';

class P {
}

const ps = [];

function pFactory() {
  return () => new P();
}

@Component({
  moduleId: module.id,
  providers: [{provide: pFactory, deps: [], useFactory: (pFactory)}],
  selector: 'my-app',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  name = 'Angular';

  constructor(@Inject(pFactory) pf) {
    let fn = pf.get(pFactory)
    ps.push(fn());
    ps.push(fn());
    console.log(ps[0] === ps[1]); // false
  }
}
Chuma
  • 51
  • 4