Reputation: 13611
I have a component that declares the MetricsService
service. This service requires both HttpModule
plus two string
s that defines the host and the auth key to use.
The metrics service is as follows:
@Injectable()
export class MetricsService {
constructor(
private http: Http,
public wsAuthKey: string,
public wsHost: string
) {
this.wsAuthKey = wsAuthKey || "blahblahblahblahblahblahblahblah=";
this.wsHost = wsHost || "https://preprod-admin.myservice.ws";
}
The component that uses it is written as follows:
export class DatavizComponent implements OnInit, OnChanges {
constructor(
private zms: MetricsService,
) {
}
My question is how should I write the component constructor so that the whole thing works, including passing the host and key (but not passing the http)?
Note : The code as currently written does not compile.
To be more precise, I would expect the component to provided app-depending data something like this:
export class DatavizComponent implements OnInit, OnChanges {
constructor(
private zms = MetricsService("http://myhost.com", "mykey"),
) {
}
But if this works, how to pass http?
UPDATE AFTER PROPOSED SOLUTION:
export class MetricsService {
constructor(
private http: Http,
@Inject('wsAuthKey') @Optional() public wsAuthKey?: string,
@Inject('wsHost') @Optional() public wsHost?: string
) {
this.wsAuthKey = wsAuthKey || "blahblah=";
this.wsHost = wsHost || "https://preprod-admin.host.ws";
console.log("MetricsService constructor="
+ " wsAuthKey="+this.wsAuthKey
+ ", wsHost="+this.wsHost
);
}
In the component:
@Component({
selector: 'dataviz-offers-volumes',
templateUrl: 'app/dataviz.component.html',
styleUrls: ['app/dataviz.component.css'],
encapsulation: ViewEncapsulation.None,
providers: [
{provide: 'wsAuthKey', useValue: 'abc'},
{provide: 'wsHost', useValue: 'efg'},
],
})
export class DatavizComponent implements OnInit, OnChanges {
@ViewChild('chart') private chartContainer: ElementRef;
@Input() private graphId:string;
@Input() private wsAuthKey:string;
@Input() private wsHost:string;
@Input() private maxSamples=12;
constructor(
private zms: MetricsService
) {
}
In the constructor, the log are as follows (value are not passed):
MetricsService constructor= wsAuthKey=blahblah=, wsHost=https://preprod-admin.host.ws
where it should show 'abc' and 'efg'.
But I wonder if there is not an issue with the component that use dataviz componenet. In this component, the following information have been passed:
@Input() private wsAuthKey:string;
@Input() private wsHost:string;
As I would like the tag to optionally preset the host and key:
<h1>dataviz volume</h1>
<div class="chartContainer left" title="Simultaneous offers via dataviz directive">
<dataviz-offers-volumes
id="dataviz-volumes1"
[graphId]="graphId"
[wsAuthKey]="'myauthkey'"
[wsHost]="'http://myhost.com'"
[maxSamples]="123"
>
</dataviz-offers-volumes>
</div>
Upvotes: 48
Views: 70328
Reputation: 7555
From the official docs: https://angular.io/guide/dependency-injection-in-action#injectiontoken
Use an @Optional
decorator in the constructor:
export class MyService {
constructor( @Optional() public var: type = value ) { }
}
Upvotes: 3
Reputation: 658235
You can make the parameters optional by adding @Optional()
(DI) and ?
(TypeScript), and @Inject(somekey)
for primitive values that are not supported as provider keys
@Injectable()
export class MetricsService {
constructor(
private http: Http,
@Inject('wsAuthKey') @Optional() public wsAuthKey?: string,
@Inject('wsHost') @Optional() public wsHost?: string
) {
this.wsAuthKey = wsAuthKey || "blahblahblahblahblahblahblahblah=";
this.wsHost = wsHost || "https://preprod-admin.myservice.ws";
}
providers: [
{provide: 'wsAuthKey', useValue: 'abc'},
{provide: 'wsHost', useValue: 'efg'},
]
If they are provided, they are passed, otherwise they are ignored, but DI still can inject the MetricsService
.
Upvotes: 54
Reputation: 223259
This is a common recipe that is described in this question in particular. It should be a service that holds the configuration:
@Injectable()
export class MetricsConfig {
wsAuthKey = "blahblahblahblahblahblahblahblah=";
wsHost = "https://preprod-admin.myservice.ws";
}
@Injectable()
export class MetricsService {
constructor(
private http: Http,
metricsConfig: MetricsConfig
) {
this.wsAuthKey = metricsConfig.wsAuthKey;
this.wsHost = metricsConfig.wsHost;
}
}
In the case when it needs to changed, it can be overridden or extended for entire module or for particular component:
@Component(
...
{ provide: MetricsConfig, useClass: class ExtendedMetricsConfig { ... } }
)
export class DatavizComponent ...
There's no real need to make MetricsConfig
a class in this case. It can be an OpaqueToken value provider as well. But a class can be conveniently extended, it is easier to inject and already provides an interface for typing.
Upvotes: 21