Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): make injector.get() return default value with InjectFlags.… #27739

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/core/src/di/injector.ts
Expand Up @@ -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;
}
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/di/r3_injector.ts
Expand Up @@ -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] || [];
Expand Down
31 changes: 30 additions & 1 deletion packages/core/test/di/static_injector_spec.ts
Expand Up @@ -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';

Expand Down Expand Up @@ -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>(Car, null, InjectFlags.Self)).toBeNull();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above implementation is not quite right If you add additional tests.

expect(() => injector.get<Car|null>(Car, undefined, InjectFlags.Self)).toThrow('....');

You will see that it will not pass. Could you fix it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it! 🙌

Also I added some more tests. For example, should return null when there is no default value and the Self and Optional flag on.

expect(injector.get<Car>(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>(Car, null, InjectFlags.Self)).toBeNull();
expect(injector.get<Car|number>(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|null>(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', () => {
Expand Down