"Can't resolve all parameters…" with @Optional decorator

2.3k Views Asked by At

I'm getting a strange DI error. This is a piece of auto-generated code from swagger-codegen - I cannot change it. I'm trying to inject this service. However when I run ng build --aot I get the Can't resolve all parameters... DI error.

I tried to remove toe @Optional parameter altogether and it seems to work. So this seems to be the culprit.

My question: Why am I getting an error for not providing the parameter if it is optional? I'm also interested how I would go about if I wanted to actually inject this parameter as it is a primitive type.

@Injectable()
export class MyApi {
    protected basePath = 'https://localhost/';
    public defaultHeaders : Headers = new Headers();

    constructor(protected http: Http, @Optional() basePath: string) {
        if (basePath) {
            this.basePath = basePath;
        }
    }
}

NOTE: The answers of @Nildari and @PierreDuc will probably be what most people may be looking for. However, I'm looking for a solution which won't change the original implementation as it is auto-generated.

3

There are 3 best solutions below

3
On

You need to use an InjectionToken:

export const BASE_PATH: InjectionToken<string> = new InjectionToken<string>('basePath');

// note that the string value `basePath` is just a description of the `InjectionToken` 
// and not actually the base path.

The advantage of using an InjectionToken

Then you need to add an provide object to your providers array of your NgModule:

@NgModule({
    //...
    providers: [
        {provide: BASE_PATH, useValue: '/'}
    ]
})

Then you can inject this provider in your service using @Inject:

constructor(protected http: Http, @Inject(BASE_PATH)basePath: string) {
    if (basePath) {
        this.basePath = basePath;
    }
}

And you can add the @Optional decorator if you feel like it.. but if you add it to your providers array, it will always be found

4
On

If you want to inject basePath into MyApi service class you can do it like below

Add some 'path' provider to app providers and use @Inject('path') parameter decorator to inject it into MyApi class

@Injectable()
export class MyApi {
    protected basePath = 'https://localhost/';
    public defaultHeaders : Headers = new Headers();

    constructor(protected http: Http, @Inject('path') basePath: string) {
        if (basePath) {
            this.basePath = basePath;
        }
    }
}

Than in the bootstrap file of your application (@NgModule)

bootstrap('name of your app.component class', [
  MyApi, 
  provide('path', { useValue: 'any value which you want to inject'})
]);

you can use @Optional parameter to make the dependency optional.

0
On

There is a 'sort-of-a-bug' where the location of the 'unresolvable' dependency can be wrongly be reported:

I have a BusyTaskService which I use in several places to indicate something is loading.

I was getting this error:

main.js:54282 NullInjectorError: StaticInjectorError(AppModule)[SupportQuestionComponent -> BusyTaskService]: 
  StaticInjectorError(Platform: core)[SupportQuestionComponent -> BusyTaskService]: 
    NullInjectorError: No provider for BusyTaskService!

Which literally made no sense give that BusyTaskService has no dependencies and the component was providing the service to itself!

...
  providers: [ BusyTaskService ]
})
export class QuickSupportQuestionComponent implements OnInit {

    constructor(@Optional() private busyTaskService: BusyTaskService,

So how is this possible?

I looked everywhere else in my code and found a second service that also used BusyTaskService but without @Optional() and that service had no parent in the DI tree that was providing an instance. Once I made the service optional for this second service everything worked.

So if you get the following and it makes no sense:

  1. Search everywhere for where you used the service and make sure it has a provider in the DI tree.

  2. Comment out the @Optional decorator, re-compile and look to see if you get a different error message.