3

I'm getting familiar with how dependency injection works in Angular 2, but I'm trying to figure out if a specific use case is possible.

In short I'd like to have a child component that has a dependency injected like so:

@Component( {
    selector: 'child',
    template: `
        <div class="child">
            <span><a href="#" (click)="idService.regenerate()">Regenerate</a></span>
            <span>{{idService.id}}</span>
        </div>
    `
})
export class ChildComponent {

    constructor(
        protected idService: IdService
    )
    {}
}

At higher levels of my component tree I am using providers: [IdService] to inject different instances of the service. This all makes sense and doesn't give me a problem. However, what I'd like to understand is if this is possible without creating a "copy" of the child component with its own providers: [...] property in the component annotation:

https://plnkr.co/edit/ZMYNIH7lB0Oi6EBSYmfB?p=preview

In that example you'll see the bottom set of components each get a new instance of the IdService class, but it's because they're really a new component that specifically requests a new instance.

What I'd like to know is if there's a way to achieve this simply with the parent component? Like being able to say "give a new instance each time a child component tries to resolve this specific dependency"?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Sam Storie
  • 4,444
  • 4
  • 48
  • 74

2 Answers2

4

If you want multiple instances (Angular DI creates singletons by default and always returns the same instance) you can use a factory.

(Example code from my answer to https://stackoverflow.com/a/35985703/217408)

@Injectable()
export class User {
    firstName:string;
    lastName:string;
    constructor(user:User, _http:Http){
        this.firstName = User.firstName;
        this.lastName = User.lastName;
    }
    getUserFirstName(){
        return this.firstName;
    }

    addUser(){
        return this._http.post('/api/user/',this);
    }
}

bootstrap(AppComponent, [OtherProviders, HTTP_PROVIDERS, 
    provide(User, {useFactory: 
        () => return (http) => new User(http);}, 
        deps: [Http])]);
export class MyComponent {
  consturctor(@Inject(User) private userFactory) {
    // create new instances by calling the factory
    let user = this.userFactory();
  }
}
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Yes, this is true but with this approach I am still restricted to a single instance at any given level of the tree. If I want unique instances at the child level I need to provide the userFactory there. I am hoping to know if I can control when an instance is created at a higher level, but not have it always be a singleton at that level. – Sam Storie Mar 17 '16 at 11:07
  • Seems I missed your comment. This way `let user = this.userFactory()` creates a new instance for each call no matter at which level. DI only maintains a single instance of the factory function but not of the instances it creates. – Günter Zöchbauer May 04 '16 at 12:39
1

I don't think that it's possible because of the way hierarchical injectors work.

If a component finds a provider within its associated injector, it will use it to get / create the service. If not it will have a look at the parent injector (the injector associated with the parent component) and so on. This is done from the bottom to the top and not the top to the bottom.

If the provider is found in the parent component, all children will use the same instance since the latter is a singleton at this level.

See this question for more details about hierarchical injectors:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360