diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts index 02ecfd46e6f08..6efee5d6f7e86 100644 --- a/packages/core/src/di/injector.ts +++ b/packages/core/src/di/injector.ts @@ -342,6 +342,10 @@ function resolveToken( } } else if (!(flags & InjectFlags.Self)) { value = parent.get(token, notFoundValue, InjectFlags.Default); + } else if (!(flags & InjectFlags.Optional)) { + value = Injector.NULL.get(token, notFoundValue); + } else { + value = Injector.NULL.get(token, typeof notFoundValue !== 'undefined' ? notFoundValue : null); } return value; } diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index df5be04609bd3..f944eab19b94f 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -197,7 +197,12 @@ export class R3Injector { // Select the next injector based on the Self flag - if self is set, the next injector is // the NullInjector, otherwise it's the parent. const nextInjector = !(flags & InjectFlags.Self) ? this.parent : getNullInjector(); - return nextInjector.get(token, flags & InjectFlags.Optional ? null : notFoundValue); + // Set the notFoundValue based on the Optional flag - if optional is set and notFoundValue + // is undefined, the value is null, otherwise it's the notFoundValue. + notFoundValue = (flags & InjectFlags.Optional) && notFoundValue === THROW_IF_NOT_FOUND ? + null : + notFoundValue; + return nextInjector.get(token, notFoundValue); } catch (e) { if (e.name === 'NullInjectorError') { const path: any[] = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || []; diff --git a/packages/core/test/di/static_injector_spec.ts b/packages/core/test/di/static_injector_spec.ts index 6d4920006e71b..7ca5da9ef7589 100644 --- a/packages/core/test/di/static_injector_spec.ts +++ b/packages/core/test/di/static_injector_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, InjectionToken, Injector, Optional, Self, SkipSelf, forwardRef} from '@angular/core'; +import {Inject, InjectFlags, InjectionToken, Injector, Optional, Self, SkipSelf, forwardRef} from '@angular/core'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, modifiedInIvy} from '@angular/private/testing'; @@ -421,6 +421,35 @@ function factoryFn(a: any){} `${injectorName}Error[${stringify(Car)} -> ${stringify(Engine)}]: \n` + ' NullInjectorError: No provider for Engine!'); }); + + it('should return a default value when not requested provider on self', () => { + const car = new SportsCar(new Engine()); + const injector = Injector.create([]); + expect(injector.get(Car, null, InjectFlags.Self)).toBeNull(); + expect(injector.get(Car, car, InjectFlags.Self)).toBe(car); + }); + + it('should return a default value when not requested provider on self and optional', () => { + const flags = InjectFlags.Self | InjectFlags.Optional; + const injector = Injector.create([]); + expect(injector.get(Car, null, InjectFlags.Self)).toBeNull(); + expect(injector.get(Car, 0, flags)).toBe(0); + }); + + it(`should return null when not requested provider on self and optional`, () => { + const flags = InjectFlags.Self | InjectFlags.Optional; + const injector = Injector.create([]); + expect(injector.get(Car, undefined, flags)).toBeNull(); + }); + + it('should throw error when not requested provider on self', () => { + const injector = Injector.create([]); + const injectorName = ivyEnabled ? `R3Injector` : `StaticInjector`; + expect(() => injector.get(Car, undefined, InjectFlags.Self)) + .toThrowError( + `${injectorName}Error[${stringify(Car)}]: \n` + + ` NullInjectorError: No provider for ${stringify(Car)}!`); + }); }); describe('default', () => {