Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 dynamic dependency injection based on @Input()

Suppose I have an Angular 2 component-directive, where I want the injected dependency that the component uses to be determined by an @Input().

I want to write something like <trendy-directive use="'serviceA'"> and have that instance of TrendyDirective use serviceA, or have it use serviceB if that's what I specify. (this is an oversimplified version of what I'm actually trying to do)

(If you think this is a terrible idea to begin with, I'm open to that feedback, but please explain why.)

Here's one example of how to achieve what I'm thinking of. In this example, imagine that ServiceA and ServiceB are injectables that both implement iService by having a 'superCoolFunction'.

@Component({     selector: 'trendy-directive',     ... }) export class TrendyDirective implements OnInit {     constructor(         private serviceA: ServiceA,         private serviceB: ServiceB){}      private service: iService;     @Input() use: string;      ngOnInit() {         switch (this.use){             case: 'serviceA': this.service = this.serviceA; break;             case: 'serviceB': this.service = this.serviceB; break;             default: throw "There's no such thing as a " + this.use + '!';         }         this.service.superCoolFunction();     } } 

I think this technically would work, but there's got to be a better way to do dynamic dependency injection.

like image 660
John Avatar asked Dec 28 '16 16:12

John


2 Answers

It is

// can be a service also for overriding and testing export const trendyServiceMap = {   serviceA: ServiceA,   serviceB: ServiceB }  constructor(private injector: Injector) {}     ... ngOnInit() {     if (trendyServiceMap.hasOwnProperty(this.use)) {         this.service = this.injector.get<any>(trendyServiceMap[this.use]);     } else {         throw new Error(`There's no such thing as '${this.use}'`);     } } 
like image 161
Estus Flask Avatar answered Sep 19 '22 03:09

Estus Flask


In general, same approach is described in Angular2 documentation: InjectorComponent

@Component({     providers: [Car, Engine, Tires, heroServiceProvider, Logger] }) export class InjectorComponent {      car: Car = this.injector.get(Car);      heroService: HeroService = this.injector.get(HeroService);      hero: Hero = this.heroService.getHeroes()[0];       constructor(private injector: Injector) { } } 

You must inject Injector in constructor and list all services in providers property of @Component annotation. Then you can injector.get(type), where type will be resolved from your @Input. As per documentation, Service is not actually injected until you ask for it (.get()).

like image 27
A. Tim Avatar answered Sep 18 '22 03:09

A. Tim