Skip to content

Commit bb7c804

Browse files
committedJun 17, 2022
fix(core): make parent injector argument required in createEnvironmentInjector (#46397)
Previously, the `createEnvironmentInjector` function allowed creating an instance of an EnvironmentInjector without providing a parent injector. This resulted in an injector instance, which was detached from the DI tree, thus having limited value. This commit updates the types of the `createEnvironmentInjector` function to make the parent injector a required argument. PR Close #46397
1 parent 82acbf9 commit bb7c804

File tree

6 files changed

+58
-28
lines changed

6 files changed

+58
-28
lines changed
 

‎goldens/public-api/core/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ export interface ContentChildrenDecorator {
289289
}
290290

291291
// @public
292-
export function createEnvironmentInjector(providers: Array<Provider | ImportedNgModuleProviders>, parent?: EnvironmentInjector | null, debugName?: string | null): EnvironmentInjector;
292+
export function createEnvironmentInjector(providers: Array<Provider | ImportedNgModuleProviders>, parent: EnvironmentInjector, debugName?: string | null): EnvironmentInjector;
293293

294294
// @public
295295
export function createNgModuleRef<T>(ngModule: Type<T>, parentInjector?: Injector): NgModuleRef<T>;

‎packages/core/src/render3/ng_module_ref.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,19 @@ class EnvironmentNgModuleRefAdapter extends viewEngine_NgModuleRef<null> {
142142
/**
143143
* Create a new environment injector.
144144
*
145+
* Learn more about environment injectors in
146+
* [this guide](guide/standalone-components#environment-injectors).
147+
*
148+
* @param providers An array of providers.
149+
* @param parent A parent environment injector.
150+
* @param debugName An optional name for this injector instance, which will be used in error
151+
* messages.
152+
*
145153
* @publicApi
146154
* @developerPreview
147155
*/
148156
export function createEnvironmentInjector(
149-
providers: Array<Provider|ImportedNgModuleProviders>, parent: EnvironmentInjector|null = null,
157+
providers: Array<Provider|ImportedNgModuleProviders>, parent: EnvironmentInjector,
150158
debugName: string|null = null): EnvironmentInjector {
151159
const adapter = new EnvironmentNgModuleRefAdapter(providers, parent, debugName);
152160
return adapter.injector;

‎packages/core/test/acceptance/di_spec.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {CommonModule} from '@angular/common';
10-
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, createEnvironmentInjector, Directive, ElementRef, ENVIRONMENT_INITIALIZER, EventEmitter, forwardRef, Host, HostBinding, ImportedNgModuleProviders, importProvidersFrom, ImportProvidersSource, inject, Inject, Injectable, InjectFlags, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewChild, ViewContainerRef, ViewRef, ɵcreateInjector as createInjector, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵINJECTOR_SCOPE} from '@angular/core';
10+
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, createEnvironmentInjector, Directive, ElementRef, ENVIRONMENT_INITIALIZER, EnvironmentInjector, EventEmitter, forwardRef, Host, HostBinding, ImportedNgModuleProviders, importProvidersFrom, ImportProvidersSource, inject, Inject, Injectable, InjectFlags, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewChild, ViewContainerRef, ViewRef, ɵcreateInjector as createInjector, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵINJECTOR_SCOPE} from '@angular/core';
1111
import {ViewRef as ViewRefInternal} from '@angular/core/src/render3/view_ref';
1212
import {TestBed} from '@angular/core/testing';
1313
import {By} from '@angular/platform-browser';
@@ -210,7 +210,8 @@ describe('importProvidersFrom', () => {
210210
expect(hasProviderWithToken(providers, C)).toBe(true);
211211
expect(hasProviderWithToken(providers, D)).toBe(true);
212212

213-
const injector = createEnvironmentInjector(providers);
213+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
214+
const injector = createEnvironmentInjector(providers, parentEnvInjector);
214215

215216
// Verify that overridden token A has the right value.
216217
expect(injector.get(A)).toBe('Overridden A');

‎packages/core/test/acceptance/env_injector_standalone_spec.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Component, createEnvironmentInjector, importProvidersFrom, InjectionToken, NgModule} from '@angular/core';
9+
import {Component, createEnvironmentInjector, EnvironmentInjector, importProvidersFrom, InjectionToken, NgModule} from '@angular/core';
10+
import {TestBed} from '@angular/core/testing';
1011

1112
import {internalImportProvidersFrom} from '../../src/di/provider_collection';
1213

@@ -22,8 +23,9 @@ describe('environment injector and standalone components', () => {
2223
class StandaloneComponent {
2324
}
2425

25-
const envInjector =
26-
createEnvironmentInjector(internalImportProvidersFrom(false, StandaloneComponent));
26+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
27+
const envInjector = createEnvironmentInjector(
28+
internalImportProvidersFrom(false, StandaloneComponent), parentEnvInjector);
2729
expect(envInjector.get(ModuleService)).toBeInstanceOf(ModuleService);
2830
});
2931

@@ -42,7 +44,9 @@ describe('environment injector and standalone components', () => {
4244
class AppModule {
4345
}
4446

45-
const envInjector = createEnvironmentInjector([importProvidersFrom(AppModule)]);
47+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
48+
const envInjector =
49+
createEnvironmentInjector([importProvidersFrom(AppModule)], parentEnvInjector);
4650
expect(envInjector.get(ModuleService)).toBeInstanceOf(ModuleService);
4751
});
4852

@@ -68,7 +72,9 @@ describe('environment injector and standalone components', () => {
6872
class AppModule {
6973
}
7074

71-
const envInjector = createEnvironmentInjector([importProvidersFrom(AppModule)]);
75+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
76+
const envInjector =
77+
createEnvironmentInjector([importProvidersFrom(AppModule)], parentEnvInjector);
7278
const services = envInjector.get(ModuleService) as ModuleService[];
7379

7480
expect(services.length).toBe(1);
@@ -90,7 +96,8 @@ describe('environment injector and standalone components', () => {
9096
]
9197
]
9298
];
93-
const envInjector = createEnvironmentInjector(providers);
99+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
100+
const envInjector = createEnvironmentInjector(providers, parentEnvInjector);
94101
expect(envInjector.get(A)).toBe('A');
95102
expect(envInjector.get(B)).toBe('B');
96103
expect(envInjector.get(C)).toBe('C');

‎packages/core/test/acceptance/environment_injector_spec.ts

+28-14
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88

99
import {Component, ComponentFactoryResolver, createEnvironmentInjector, ENVIRONMENT_INITIALIZER, EnvironmentInjector, InjectionToken, INJECTOR, Injector, NgModuleRef} from '@angular/core';
1010
import {R3Injector} from '@angular/core/src/di/r3_injector';
11+
import {TestBed} from '@angular/core/testing';
1112

1213
describe('environment injector', () => {
1314
it('should create and destroy an environment injector', () => {
1415
class Service {}
1516

1617
let destroyed = false;
17-
const envInjector = createEnvironmentInjector([Service]) as R3Injector;
18+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
19+
const envInjector = createEnvironmentInjector([Service], parentEnvInjector) as R3Injector;
1820
envInjector.onDestroy(() => destroyed = true);
1921

2022
const service = envInjector.get(Service);
@@ -27,38 +29,45 @@ describe('environment injector', () => {
2729
it('should see providers from a parent EnvInjector', () => {
2830
class Service {}
2931

30-
const envInjector = createEnvironmentInjector([], createEnvironmentInjector([Service]));
32+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
33+
const envInjector =
34+
createEnvironmentInjector([], createEnvironmentInjector([Service], parentEnvInjector));
3135
expect(envInjector.get(Service)).toBeInstanceOf(Service);
3236
});
3337

3438
it('should shadow providers from the parent EnvInjector', () => {
3539
const token = new InjectionToken('token');
3640

41+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
3742
const envInjector = createEnvironmentInjector(
3843
[{provide: token, useValue: 'child'}],
39-
createEnvironmentInjector([{provide: token, useValue: 'parent'}]));
44+
createEnvironmentInjector([{provide: token, useValue: 'parent'}], parentEnvInjector));
4045
expect(envInjector.get(token)).toBe('child');
4146
});
4247

4348
it('should expose the Injector token', () => {
44-
const envInjector = createEnvironmentInjector([]);
49+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
50+
const envInjector = createEnvironmentInjector([], parentEnvInjector);
4551
expect(envInjector.get(Injector)).toBe(envInjector);
4652
expect(envInjector.get(INJECTOR)).toBe(envInjector);
4753
});
4854

4955
it('should expose the EnvInjector token', () => {
50-
const envInjector = createEnvironmentInjector([]);
56+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
57+
const envInjector = createEnvironmentInjector([], parentEnvInjector);
5158
expect(envInjector.get(EnvironmentInjector)).toBe(envInjector);
5259
});
5360

5461
it('should expose the same object as both the Injector and EnvInjector token', () => {
55-
const envInjector = createEnvironmentInjector([]);
62+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
63+
const envInjector = createEnvironmentInjector([], parentEnvInjector);
5664
expect(envInjector.get(Injector)).toBe(envInjector.get(EnvironmentInjector));
5765
});
5866

5967
it('should expose the NgModuleRef token', () => {
6068
class Service {}
61-
const envInjector = createEnvironmentInjector([Service]);
69+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
70+
const envInjector = createEnvironmentInjector([Service], parentEnvInjector);
6271

6372
const ngModuleRef = envInjector.get(NgModuleRef);
6473

@@ -78,7 +87,8 @@ describe('environment injector', () => {
7887
constructor(readonly service: Service) {}
7988
}
8089

81-
const envInjector = createEnvironmentInjector([Service]);
90+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
91+
const envInjector = createEnvironmentInjector([Service], parentEnvInjector);
8292
const cfr = envInjector.get(ComponentFactoryResolver);
8393
const cf = cfr.resolveComponentFactory(TestComponent);
8494
const cRef = cf.create(Injector.NULL);
@@ -88,17 +98,21 @@ describe('environment injector', () => {
8898

8999
it('should support the ENVIRONMENT_INITIALIZER muli-token', () => {
90100
let initialized = false;
91-
createEnvironmentInjector([{
92-
provide: ENVIRONMENT_INITIALIZER,
93-
useValue: () => initialized = true,
94-
multi: true,
95-
}]);
101+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
102+
createEnvironmentInjector(
103+
[{
104+
provide: ENVIRONMENT_INITIALIZER,
105+
useValue: () => initialized = true,
106+
multi: true,
107+
}],
108+
parentEnvInjector);
96109

97110
expect(initialized).toBeTrue();
98111
});
99112

100113
it('should adopt environment-scoped providers', () => {
101-
const injector = createEnvironmentInjector([]);
114+
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
115+
const injector = createEnvironmentInjector([], parentEnvInjector);
102116
const EnvScopedToken = new InjectionToken('env-scoped token', {
103117
providedIn: 'environment' as any,
104118
factory: () => true,

‎packages/core/test/acceptance/standalone_spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ describe('standalone components, directives and pipes', () => {
303303
parent: this.inj,
304304
});
305305

306-
const rhsInj =
307-
createEnvironmentInjector([{provide: Service, useClass: EnvOverrideService}]);
306+
const rhsInj = createEnvironmentInjector(
307+
[{provide: Service, useClass: EnvOverrideService}], this.envInj);
308308

309309
this.vcRef.createComponent(InnerCmp, {injector: lhsInj, environmentInjector: rhsInj});
310310
}
@@ -352,8 +352,8 @@ describe('standalone components, directives and pipes', () => {
352352
constructor(readonly envInj: EnvironmentInjector) {}
353353

354354
ngOnInit(): void {
355-
const rhsInj =
356-
createEnvironmentInjector([{provide: Service, useClass: EnvOverrideService}]);
355+
const rhsInj = createEnvironmentInjector(
356+
[{provide: Service, useClass: EnvOverrideService}], this.envInj);
357357

358358
this.vcRef.createComponent(InnerCmp, {environmentInjector: rhsInj});
359359
}

0 commit comments

Comments
 (0)
Please sign in to comment.