42

I have a service that requires some value to be initiated:

@Injectable()
export class MyService {
  private myVals: any;

  constructor(init : any) {
    this.myVals = init;
  }
}

And the consumer:

@Component(...)
export class MyComponent {
  private svc: MyService;
  constructor(private svc : MyService) {
  }
}

So is there a way to inject and pass the required parameter to MyService's constructor "during" dependency injection??

Something like:

constructor(private svc({ // init vales }) : MyService) {}

I know I can pass by variable, but interested to find if there's a way to do this from the API.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
gt6707a
  • 649
  • 1
  • 7
  • 12

3 Answers3

26

There is an official way that Angular team recommends in here. It basically allows you to inject statically typed configuration classes.

I have successfully implemented it and here is all the relevant code:

1) app.config.ts

import { OpaqueToken } from "@angular/core";

export let APP_CONFIG = new OpaqueToken("app.config");

export interface IAppConfig {
    apiEndpoint: string;
}

export const AppConfig: IAppConfig = {    
    apiEndpoint: "http://localhost:15422/api/"    
};

2) app.module.ts

import { APP_CONFIG, AppConfig } from './app.config';

@NgModule({
    providers: [
        { provide: APP_CONFIG, useValue: AppConfig }
    ]
})

3) your.service.ts

import { APP_CONFIG, IAppConfig } from './app.config';

@Injectable()
export class YourService {

    constructor(@Inject(APP_CONFIG) private config: IAppConfig) {
             // You can use config.apiEndpoint now
    }   
}

Now you can inject the config everywhere without using the string names and with the use of your interface for static checks.

You can of course separate the Interface and the constant further to be able to supply different values in production and development e.g.

Ilya Chernomordik
  • 27,817
  • 27
  • 121
  • 207
  • 23
    That works for known static values. I'm curious if I can tell Angular to instantiate the service with values determined at run time, though, using constructor (sticking to the pattern). – gt6707a Nov 14 '16 at 18:40
  • 1
    Well, that's an ordinary service then. Just don't call it a service, and here you go :) – Ilya Chernomordik Nov 14 '16 at 18:52
  • 6
    Update!! In Angular4 OpaqueToken is deprecated and will be replaced by InjectionToken. InjectionToken allows to pass a generic type parameter. 1) https://stackoverflow.com/questions/41289264/what-is-in-angular-2-opaque-token-and-whats-the-point 2) https://arturas.smorgun.com/2017/06/08/inject-environment-configuration-into-service-in-angular4.html – Visarut Sae-Pueng Jan 10 '18 at 04:38
5

The above answer has been deprecated since angular 4 Now you can use it like this:

import { NgModule, APP_INITIALIZER } from '@angular/core';

@NgModule({
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: onAppInit1,
      multi: true,
      deps: [/* your dependencies */]
    },
    {
      provide: APP_INITIALIZER,
      useFactory: onAppInit2,
      multi: true,
      deps: [/* your dependencies */]
    }
  ]
})
export class AppModule { }
Black Mamba
  • 13,632
  • 6
  • 82
  • 105
4

You can use @Inject, explained here

In your service:

import {Inject, Injectable} from '@angular/core';
    
@Injectable({
  providedIn: 'root'
})

export class NgService {
    constructor (
       @Inject('paramId') private paramId: string
    ) { }
}

And then, in your component:

import { Component } from '@angular/core';

@Component({
  selector: 'app-demo',
  template: `
    <div"> </div>
  `,
   providers: [
    {provide: 'paramId', useValue: 'param-id'},
  ]
})

export class AppComponent {

  constructor(private ngService: NgService) { }

}
Robouste
  • 3,020
  • 4
  • 33
  • 55