Skip to content

Commit

Permalink
feat(core): support object-based DI flags in Injector.get()
Browse files Browse the repository at this point in the history
This commit applies the changes similar to the ones performed for the `inject()` function in df246bb.

The `Injector.get` function is updated to use previously added object-based API for options: now the flags argument supports passing an object which configures injection flags.

DEPRECATED:

The bit field signature of `Injector.get()` has been deprecated, in favor of the new options object.
  • Loading branch information
AndrewKushnir committed Jul 12, 2022
1 parent 55308f2 commit ccac3f5
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 57 deletions.
4 changes: 4 additions & 0 deletions goldens/public-api/core/index.md
Expand Up @@ -468,6 +468,8 @@ export const ENVIRONMENT_INITIALIZER: InjectionToken<() => void>;
export abstract class EnvironmentInjector implements Injector {
// (undocumented)
abstract destroy(): void;
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
// @deprecated
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
// @deprecated (undocumented)
abstract get(token: any, notFoundValue?: any): any;
Expand Down Expand Up @@ -707,6 +709,8 @@ export abstract class Injector {
parent?: Injector;
name?: string;
}): Injector;
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions | InjectFlags): T;
// @deprecated
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
// @deprecated (undocumented)
abstract get(token: any, notFoundValue?: any): any;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/di/index.ts
Expand Up @@ -22,7 +22,8 @@ export {EnvironmentInjector} from './r3_injector';
export {importProvidersFrom, ImportProvidersSource} from './provider_collection';
export {ENVIRONMENT_INITIALIZER} from './initializer_token';
export {ProviderToken} from './provider_token';
export {ɵɵinject, inject, InjectOptions, ɵɵinvalidFactoryDep} from './injector_compatibility';
export {ɵɵinject, inject, ɵɵinvalidFactoryDep} from './injector_compatibility';
export {InjectOptions} from './interface/injector';
export {INJECTOR} from './injector_token';
export {ReflectiveInjector} from './reflective_injector';
export {ClassProvider, ModuleWithProviders, ClassSansProvider, ImportedNgModuleProviders, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, Provider, StaticClassProvider, StaticClassSansProvider, StaticProvider, TypeProvider, ValueProvider, ValueSansProvider} from './interface/provider';
Expand Down
10 changes: 9 additions & 1 deletion packages/core/src/di/injector.ts
Expand Up @@ -12,7 +12,7 @@ import {THROW_IF_NOT_FOUND, ɵɵinject} from './injector_compatibility';
import {InjectorMarkers} from './injector_marker';
import {INJECTOR} from './injector_token';
import {ɵɵdefineInjectable} from './interface/defs';
import {InjectFlags} from './interface/injector';
import {InjectFlags, InjectOptions} from './interface/injector';
import {StaticProvider} from './interface/provider';
import {NullInjector} from './null_injector';
import {ProviderToken} from './provider_token';
Expand Down Expand Up @@ -50,6 +50,14 @@ export abstract class Injector {
* @returns The instance from the injector if defined, otherwise the `notFoundValue`.
* @throws When the `notFoundValue` is `undefined` or `Injector.THROW_IF_NOT_FOUND`.
*/
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions|InjectFlags):
T;
/**
* Retrieves an instance from the injector based on the provided token.
* @returns The instance from the injector if defined, otherwise the `notFoundValue`.
* @throws When the `notFoundValue` is `undefined` or `Injector.THROW_IF_NOT_FOUND`.
* @deprecated use object-based flags (`InjectOptions`) instead.
*/
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
/**
* @deprecated from v4.0.0 use ProviderToken<T>
Expand Down
58 changes: 17 additions & 41 deletions packages/core/src/di/injector_compatibility.ts
Expand Up @@ -15,7 +15,7 @@ import {stringify} from '../util/stringify';
import {resolveForwardRef} from './forward_ref';
import {getInjectImplementation, injectRootLimpMode} from './inject_switch';
import {Injector} from './injector';
import {DecoratorFlags, InjectFlags, InternalInjectFlags} from './interface/injector';
import {DecoratorFlags, InjectFlags, InjectOptions, InternalInjectFlags} from './interface/injector';
import {ProviderToken} from './provider_token';


Expand Down Expand Up @@ -102,35 +102,6 @@ Please check that 1) the type for the parameter at index ${
index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.`);
}

/**
* Type of the options argument to `inject`.
*
* @publicApi
*/
export interface InjectOptions {
/**
* Use optional injection, and return `null` if the requested token is not found.
*/
optional?: boolean;

/**
* Start injection at the parent of the current injector.
*/
skipSelf?: boolean;

/**
* Only query the current injector for the token, and don't fall back to the parent injector if
* it's not found.
*/
self?: boolean;

/**
* Stop injection at the host component's injector. Only relevant when injecting from an element
* injector, and a no-op for environment injectors.
*/
host?: boolean;
}

/**
* @param token A token that represents a dependency that should be injected.
* @returns the injected value if operation is successful, `null` otherwise.
Expand Down Expand Up @@ -240,17 +211,22 @@ export function inject<T>(token: ProviderToken<T>, options: InjectOptions): T|nu
*/
export function inject<T>(
token: ProviderToken<T>, flags: InjectFlags|InjectOptions = InjectFlags.Default): T|null {
if (typeof flags !== 'number') {
// While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
// JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
// `InjectOptions` to `InjectFlags`.
flags = (InternalInjectFlags.Default | // comment to force a line break in the formatter
((flags.optional && InternalInjectFlags.Optional) as number) |
((flags.host && InternalInjectFlags.Host) as number) |
((flags.self && InternalInjectFlags.Self) as number) |
((flags.skipSelf && InternalInjectFlags.SkipSelf) as number)) as InjectFlags;
}
return ɵɵinject(token, flags);
return ɵɵinject(token, convertToBitFlags(flags));
}

// Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
export function convertToBitFlags(flags: InjectOptions|InjectFlags|undefined): InjectFlags|
undefined {
if (typeof flags === 'undefined' || typeof flags === 'number') return flags;

// While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
// JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
// `InjectOptions` to `InjectFlags`.
return (InternalInjectFlags.Default | // comment to force a line break in the formatter
((flags.optional && InternalInjectFlags.Optional) as number) |
((flags.host && InternalInjectFlags.Host) as number) |
((flags.self && InternalInjectFlags.Self) as number) |
((flags.skipSelf && InternalInjectFlags.SkipSelf) as number)) as InjectFlags;
}

export function injectArgs(types: (ProviderToken<any>|any[])[]): any[] {
Expand Down
29 changes: 29 additions & 0 deletions packages/core/src/di/interface/injector.ts
Expand Up @@ -80,3 +80,32 @@ export const enum InternalInjectFlags {
*/
ForPipe = 0b10000,
}

/**
* Type of the options argument to `inject`.
*
* @publicApi
*/
export interface InjectOptions {
/**
* Use optional injection, and return `null` if the requested token is not found.
*/
optional?: boolean;

/**
* Start injection at the parent of the current injector.
*/
skipSelf?: boolean;

/**
* Only query the current injector for the token, and don't fall back to the parent injector if
* it's not found.
*/
self?: boolean;

/**
* Stop injection at the host component's injector. Only relevant when injecting from an element
* injector, and a no-op for environment injectors.
*/
host?: boolean;
}
17 changes: 13 additions & 4 deletions packages/core/src/di/r3_injector.ts
Expand Up @@ -23,14 +23,14 @@ import {ENVIRONMENT_INITIALIZER} from './initializer_token';
import {setInjectImplementation} from './inject_switch';
import {InjectionToken} from './injection_token';
import {Injector} from './injector';
import {catchInjectorError, injectArgs, NG_TEMP_TOKEN_PATH, setCurrentInjector, THROW_IF_NOT_FOUND, ɵɵinject} from './injector_compatibility';
import {catchInjectorError, convertToBitFlags, injectArgs, NG_TEMP_TOKEN_PATH, setCurrentInjector, THROW_IF_NOT_FOUND, ɵɵinject} from './injector_compatibility';
import {INJECTOR} from './injector_token';
import {getInheritedInjectableDef, getInjectableDef, InjectorType, ɵɵInjectableDeclaration} from './interface/defs';
import {InjectFlags} from './interface/injector';
import {InjectFlags, InjectOptions} from './interface/injector';
import {ClassProvider, ConstructorProvider, ImportedNgModuleProviders, Provider, StaticClassProvider} from './interface/provider';
import {INJECTOR_DEF_TYPES} from './internal_tokens';
import {NullInjector} from './null_injector';
import {importProvidersFrom, isExistingProvider, isFactoryProvider, isTypeProvider, isValueProvider, SingleProvider} from './provider_collection';
import {isExistingProvider, isFactoryProvider, isTypeProvider, isValueProvider, SingleProvider} from './provider_collection';
import {ProviderToken} from './provider_token';
import {INJECTOR_SCOPE, InjectorScope} from './scope';

Expand Down Expand Up @@ -82,6 +82,13 @@ export abstract class EnvironmentInjector implements Injector {
* @returns The instance from the injector if defined, otherwise the `notFoundValue`.
* @throws When the `notFoundValue` is `undefined` or `Injector.THROW_IF_NOT_FOUND`.
*/
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
/**
* Retrieves an instance from the injector based on the provided token.
* @returns The instance from the injector if defined, otherwise the `notFoundValue`.
* @throws When the `notFoundValue` is `undefined` or `Injector.THROW_IF_NOT_FOUND`.
* @deprecated use object-based flags (`InjectOptions`) instead.
*/
abstract get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
/**
* @deprecated from v4.0.0 use ProviderToken<T>
Expand Down Expand Up @@ -207,8 +214,10 @@ export class R3Injector extends EnvironmentInjector {

override get<T>(
token: ProviderToken<T>, notFoundValue: any = THROW_IF_NOT_FOUND,
flags = InjectFlags.Default): T {
flags: InjectFlags|InjectOptions = InjectFlags.Default): T {
this.assertNotDestroyed();
flags = convertToBitFlags(flags) as InjectFlags;

// Set the injection context.
const previousInjector = setCurrentInjector(this);
const previousInjectImplementation = setInjectImplementation(undefined);
Expand Down
10 changes: 6 additions & 4 deletions packages/core/src/render3/component_ref.ts
Expand Up @@ -8,10 +8,11 @@

import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {Injector} from '../di/injector';
import {InjectFlags} from '../di/interface/injector';
import {convertToBitFlags} from '../di/injector_compatibility';
import {InjectFlags, InjectOptions} from '../di/interface/injector';
import {ProviderToken} from '../di/provider_token';
import {EnvironmentInjector} from '../di/r3_injector';
import {formatRuntimeError, RuntimeError, RuntimeErrorCode} from '../errors';
import {RuntimeError, RuntimeErrorCode} from '../errors';
import {Type} from '../interface/type';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
Expand All @@ -27,7 +28,7 @@ import {createRootComponent, createRootComponentView, createRootContext, Lifecyc
import {getComponentDef} from './definition';
import {NodeInjector} from './di';
import {reportUnknownPropertyError} from './instructions/element_validation';
import {createLView, createTView, initializeInputAndOutputAliases, locateHostElement, markDirtyIfOnPush, renderView, setInputsForProperty} from './instructions/shared';
import {createLView, createTView, locateHostElement, markDirtyIfOnPush, renderView, setInputsForProperty} from './instructions/shared';
import {ComponentDef} from './interfaces/definition';
import {PropertyAliasValue, TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node';
import {RNode} from './interfaces/renderer_dom';
Expand Down Expand Up @@ -79,7 +80,8 @@ function getNamespace(elementName: string): string|null {
class ChainedInjector implements Injector {
constructor(private injector: Injector, private parentInjector: Injector) {}

get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T {
get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags|InjectOptions): T {
flags = convertToBitFlags(flags);
const value = this.injector.get<T|typeof NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR>(
token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);

Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/render3/di.ts
Expand Up @@ -9,8 +9,9 @@
import {isForwardRef, resolveForwardRef} from '../di/forward_ref';
import {injectRootLimpMode, setInjectImplementation} from '../di/inject_switch';
import {Injector} from '../di/injector';
import {convertToBitFlags} from '../di/injector_compatibility';
import {InjectorMarkers} from '../di/injector_marker';
import {InjectFlags} from '../di/interface/injector';
import {InjectFlags, InjectOptions} from '../di/interface/injector';
import {ProviderToken} from '../di/provider_token';
import {Type} from '../interface/type';
import {assertDefined, assertEqual, assertIndexInRange} from '../util/assert';
Expand Down Expand Up @@ -704,8 +705,9 @@ export class NodeInjector implements Injector {
private _tNode: TElementNode|TContainerNode|TElementContainerNode|null,
private _lView: LView) {}

get(token: any, notFoundValue?: any, flags?: InjectFlags): any {
return getOrCreateInjectable(this._tNode, this._lView, token, flags, notFoundValue);
get(token: any, notFoundValue?: any, flags?: InjectFlags|InjectOptions): any {
return getOrCreateInjectable(
this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
}
}

Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/render3/ng_module_ref.ts
Expand Up @@ -8,8 +8,9 @@

import {createInjectorWithoutInjectorInstances} from '../di/create_injector';
import {Injector} from '../di/injector';
import {convertToBitFlags} from '../di/injector_compatibility';
import {INJECTOR} from '../di/injector_token';
import {InjectFlags} from '../di/interface/injector';
import {InjectFlags, InjectOptions} from '../di/interface/injector';
import {ImportedNgModuleProviders, Provider} from '../di/interface/provider';
import {EnvironmentInjector, getNullInjector, R3Injector} from '../di/r3_injector';
import {Type} from '../interface/type';
Expand Down Expand Up @@ -90,11 +91,11 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
}

get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND,
injectFlags: InjectFlags = InjectFlags.Default): any {
injectFlags: InjectFlags|InjectOptions = InjectFlags.Default): any {
if (token === Injector || token === viewEngine_NgModuleRef || token === INJECTOR) {
return this;
}
return this._r3Injector.get(token, notFoundValue, injectFlags);
return this._r3Injector.get(token, notFoundValue, convertToBitFlags(injectFlags));
}

runInContext<ReturnT>(fn: () => ReturnT): ReturnT {
Expand Down

0 comments on commit ccac3f5

Please sign in to comment.