I have authored a library for extracting environment variables that are set from server-side rendering.
This library provides a base class to extend. getEnvironmentValues()
has been omitted for brevity:
@Directive()
export class NgxEnvironmentService<T> {
environment: T;
constructor(
@Inject(ENVIRONMENT_CONFIG)
private readonly environmentConfig: IEnvironmentConfig,
@Inject(PLATFORM_ID)
private readonly platformId: string,
) {
if (isPlatformBrowser(this.platformId)) {
this.environment = this.getEnvironmentValues<T>();
}
}
}
Here is the ENVIRONMENT_CONFIG
token:
import { InjectionToken } from '@angular/core';
import { DEFAULT_CONFIG } from './constants';
import { IEnvironmentConfig } from '../interfaces';
export const ENVIRONMENT_CONFIG = new InjectionToken<IEnvironmentConfig>('environment-config', {
factory: (): IEnvironmentConfig => DEFAULT_CONFIG,
providedIn: 'root'
});
This library is transpiled using ng-packagr. After installation and implementation in the target project:
import { Injectable } from '@angular/core';
import { NgxEnvironmentService } from '@labcorp/ngx-environment';
import { IEnvironment } from '../interfaces';
@Injectable({
providedIn: 'root'
})
export class EnvironmentService extends NgxEnvironmentService<IEnvironment> {}
I receive the following error:
The injectable EnvironmentService inherits its constructor from NgxEnvironmentService, but the latter has a constructor parameter that is not compatible with dependency injection. Either add an explicit constructor to EnvironmentService or change NgxEnvironmentService's constructor to use parameters that are valid for DI.
If I take the source code from the library and copy it into the target project and change the import path, everything works as expected.
This issue: https://stackoverflow.com/questions/60702258/angular-ivy-constructor-is-not-compatible-with-angular-dependency-injection#:~:text=core.js%3A3828%20ERROR%20Error%3A%20This%20constructor%20is%20not%20compatible,of%20this%20class%20is%20missing%20an%20Angular%20decorator is similar, but I'd really like to avoid having to re-implement the constructor in classes that extend NgxEnvironmentService
Has anyone else experienced this error, and how did you fix it?
There were two underlying problems causing this behavior.
First was this issue: https://github.com/angular/angular/issues/45155 which was resolved with the release of Angular 15.0.1: https://github.com/angular/angular/issues/46419
Setting strictInjectionParameters
to false
in my target project fixed the compiler issue.
Second was that during testing, I was specifying a local install path for the library in my target project. Even though I had not included @angular/core
as a dependency
in the library's package.json, the target project was apparently confused as to which @angular/core
to use, resulting in this error at runtime:
ERROR Error: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `EnvironmentInjector#runInContext`. Find more at https://angular.io/errors/NG0203
at injectInjectorOnly (core.mjs:731:15)
at Module.ɵɵinject (core.mjs:742:60)
at NgxEnvironmentService_Factory (ngx-environment.service.ts:11:35)
at Object.EnvironmentService_Factory [as factory] (environment.service.ts:9:32)
at R3Injector.hydrate (core.mjs:8780:35)
at R3Injector.get (core.mjs:8668:33)
at ChainedInjector.get (core.mjs:13929:36)
at lookupTokenUsingModuleInjector (core.mjs:3547:39)
at getOrCreateInjectable (core.mjs:3592:12)
at Module.ɵɵdirectiveInject (core.mjs:10971:12)
I set a paths
value in my tsconfig.json
:
"paths": {
"@angular/*": ["node_modules/@angular/*"],
}
which fixed the runtime error, however, after publishing my library to npm and installing from there, I was able to remove the path and the project now runs as expected.