Brandon Brandon - 1 month ago 16
TypeScript Question

Injected dependency is undefined when extending BaseRequestOptions

I am extending

BaseRequestOptions
in Angular2 to add headers for each request. I also have a
Config
class that supplies key/value pairs based on the domain, which I need to inject into my derived class:

import { BaseRequestOptions } from '@angular/http';
import { Config } from '../../config/configuration';

export class DefaultRequestOptions extends BaseRequestOptions {
constructor(private config: Config) {
super();

Object.keys(this.config.api.headers).map((key) => {
this.headers.append(key, this.config.api.headers[key]);
});
}
}


In my module, I am specifying the provider as such:

@NgModule({
. . .,
providers: [
. . .,
{ provide: RequestOptions, useClass: DefaultRequestOptions }
]
})
. . .


The problem I'm having is that
this.config
is
undefined
in
DefaultRequestOptions
. I am also using the
Config
class as an injected dependency in other classes, so I'm confident it's working as expected, and if I manually set the values for
this.headers
everything works fine.

What am I doing wrong that would cause the config to be undefined in
DefaultRequestOptions
?

Answer

Provider classes don't require @Injectable() unless they require constructor parameters to be injected. In your case it does. This is why Angular recommends to always use it on your providers, whether or not you require injections. Maybe for this exact reason, where you forget, when it's needed.

@Injectable()
export class DefaultRequestOptions extends BaseRequestOptions {

After testing

It seems this still doesn't work. I've seen this problem is a result of extending a class that already uses @Injectable() (which BaseRequestOptions does). In this case, the parent constructor is called instead of the extending class'. If you extend RequestOptions instead (which is not decorated with @Injectable()) then it would work

@Injectable()
class DefaultRequestOptions extends RequestOptions {
  constructor(public config: Config) {
    super({method: RequestMethod.Get, headers: new Headers()})

    this.headers.append('data', this.config.data);
  }
}

Notice the super() call. This is all that BaseRequestOptions does.

If you wanted to keep it the way it currently is, using BaseRequestOptions, then you could use a factory, instead of letting Angular create it

{
  provide: RequestOptions,
  deps: [ Config ],
  useFactory: (config: Config) => {
    return new DefaultRequestOptions(config);
  }
}
Comments