/** * @license Angular v19.2.13 * (c) 2010-2025 Google LLC. https://angular.io/ * License: MIT */ import { SIGNAL_NODE, signalSetFn, SIGNAL, producerAccessed, getActiveConsumer, setActiveConsumer as setActiveConsumer$1, createSignal, signalUpdateFn, consumerDestroy, REACTIVE_NODE, consumerPollProducersForChange, consumerBeforeComputation, consumerAfterComputation, createComputed, setThrowInvalidWriteToSignalError, untracked as untracked$1, isInNotificationPhase, createLinkedSignal, linkedSignalSetFn, linkedSignalUpdateFn } from './untracked-BKcld_ew.mjs'; export { setAlternateWeakRefImpl as ɵsetAlternateWeakRefImpl } from './untracked-BKcld_ew.mjs'; import { getCurrentInjector, NOT_FOUND as NOT_FOUND$1, setCurrentInjector } from './primitives/di.mjs'; import { setActiveConsumer, createWatch } from '@angular/core/primitives/signals'; import { NOT_FOUND as NOT_FOUND$2 } from '@angular/core/primitives/di'; import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { Attribute as Attribute$1, clearAppScopedEarlyEventContract, EventContract, EventContractContainer, getAppScopedQueuedEventInfos, EventDispatcher, registerDispatcher, EventPhase, isEarlyEventType, isCaptureEventType } from './primitives/event-dispatch.mjs'; import { map } from 'rxjs/operators'; /** * Base URL for the error details page. * * Keep this constant in sync across: * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts * - packages/core/src/error_details_base_url.ts */ const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.dev/errors'; /** * URL for the XSS security documentation. */ const XSS_SECURITY_URL = 'https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss'; /** * Class that represents a runtime error. * Formats and outputs the error message in a consistent way. * * Example: * ```ts * throw new RuntimeError( * RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED, * ngDevMode && 'Injector has already been destroyed.'); * ``` * * Note: the `message` argument contains a descriptive error message as a string in development * mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the * `message` argument becomes `false`, thus we account for it in the typings and the runtime * logic. */ class RuntimeError extends Error { code; constructor(code, message) { super(formatRuntimeError(code, message)); this.code = code; } } function formatRuntimeErrorCode(code) { // Error code might be a negative number, which is a special marker that instructs the logic to // generate a link to the error details page on angular.io. // We also prepend `0` to non-compile-time errors. return `NG0${Math.abs(code)}`; } /** * Called to format a runtime error. * See additional info on the `message` argument type in the `RuntimeError` class description. */ function formatRuntimeError(code, message) { const fullCode = formatRuntimeErrorCode(code); let errorMessage = `${fullCode}${message ? ': ' + message : ''}`; if (ngDevMode && code < 0) { const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/); const separator = addPeriodSeparator ? '.' : ''; errorMessage = `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`; } return errorMessage; } const REQUIRED_UNSET_VALUE = /* @__PURE__ */ Symbol('InputSignalNode#UNSET'); // Note: Using an IIFE here to ensure that the spread assignment is not considered // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved. const INPUT_SIGNAL_NODE = /* @__PURE__ */ (() => { return { ...SIGNAL_NODE, transformFn: undefined, applyValueToInputSignal(node, value) { signalSetFn(node, value); }, }; })(); const ɵINPUT_SIGNAL_BRAND_WRITE_TYPE = /* @__PURE__ */ Symbol(); /** * Creates an input signal. * * @param initialValue The initial value. * Can be set to {@link REQUIRED_UNSET_VALUE} for required inputs. * @param options Additional options for the input. e.g. a transform, or an alias. */ function createInputSignal(initialValue, options) { const node = Object.create(INPUT_SIGNAL_NODE); node.value = initialValue; // Perf note: Always set `transformFn` here to ensure that `node` always // has the same v8 class shape, allowing monomorphic reads on input signals. node.transformFn = options?.transform; function inputValueFn() { // Record that someone looked at this signal. producerAccessed(node); if (node.value === REQUIRED_UNSET_VALUE) { let message = null; if (ngDevMode) { const name = options?.debugName ?? options?.alias; message = `Input${name ? ` "${name}"` : ''} is required but no value is available yet.`; } throw new RuntimeError(-950 /* RuntimeErrorCode.REQUIRED_INPUT_NO_VALUE */, message); } return node.value; } inputValueFn[SIGNAL] = node; if (ngDevMode) { inputValueFn.toString = () => `[Input Signal: ${inputValueFn()}]`; node.debugName = options?.debugName; } return inputValueFn; } /** * Convince closure compiler that the wrapped function has no side-effects. * * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to * allow us to execute a function but have closure compiler mark the call as no-side-effects. * It is important that the return value for the `noSideEffects` function be assigned * to something which is retained otherwise the call to `noSideEffects` will be removed by closure * compiler. */ function noSideEffects(fn) { return { toString: fn }.toString(); } const ANNOTATIONS = '__annotations__'; const PARAMETERS = '__parameters__'; const PROP_METADATA = '__prop__metadata__'; /** * @suppress {globalThis} */ function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function DecoratorFactory(...args) { if (this instanceof DecoratorFactory) { metaCtor.call(this, ...args); return this; } const annotationInstance = new DecoratorFactory(...args); return function TypeDecorator(cls) { if (typeFn) typeFn(cls, ...args); // Use of Object.defineProperty is important since it creates non-enumerable property which // prevents the property is copied during subclassing. const annotations = cls.hasOwnProperty(ANNOTATIONS) ? cls[ANNOTATIONS] : Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS]; annotations.push(annotationInstance); return cls; }; } if (parentClass) { DecoratorFactory.prototype = Object.create(parentClass.prototype); } DecoratorFactory.prototype.ngMetadataName = name; DecoratorFactory.annotationCls = DecoratorFactory; return DecoratorFactory; }); } function makeMetadataCtor(props) { return function ctor(...args) { if (props) { const values = props(...args); for (const propName in values) { this[propName] = values[propName]; } } }; } function makeParamDecorator(name, props, parentClass) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function ParamDecoratorFactory(...args) { if (this instanceof ParamDecoratorFactory) { metaCtor.apply(this, args); return this; } const annotationInstance = new ParamDecoratorFactory(...args); ParamDecorator.annotation = annotationInstance; return ParamDecorator; function ParamDecorator(cls, unusedKey, index) { // Use of Object.defineProperty is important since it creates non-enumerable property which // prevents the property is copied during subclassing. const parameters = cls.hasOwnProperty(PARAMETERS) ? cls[PARAMETERS] : Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS]; // there might be gaps if some in between parameters do not have annotations. // we pad with nulls. while (parameters.length <= index) { parameters.push(null); } (parameters[index] = parameters[index] || []).push(annotationInstance); return cls; } } ParamDecoratorFactory.prototype.ngMetadataName = name; ParamDecoratorFactory.annotationCls = ParamDecoratorFactory; return ParamDecoratorFactory; }); } function makePropDecorator(name, props, parentClass, additionalProcessing) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function PropDecoratorFactory(...args) { if (this instanceof PropDecoratorFactory) { metaCtor.apply(this, args); return this; } const decoratorInstance = new PropDecoratorFactory(...args); function PropDecorator(target, name) { // target is undefined with standard decorators. This case is not supported and will throw // if this decorator is used in JIT mode with standard decorators. if (target === undefined) { throw new Error('Standard Angular field decorators are not supported in JIT mode.'); } const constructor = target.constructor; // Use of Object.defineProperty is important because it creates a non-enumerable property // which prevents the property from being copied during subclassing. const meta = constructor.hasOwnProperty(PROP_METADATA) ? constructor[PROP_METADATA] : Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA]; meta[name] = (meta.hasOwnProperty(name) && meta[name]) || []; meta[name].unshift(decoratorInstance); } return PropDecorator; } if (parentClass) { PropDecoratorFactory.prototype = Object.create(parentClass.prototype); } PropDecoratorFactory.prototype.ngMetadataName = name; PropDecoratorFactory.annotationCls = PropDecoratorFactory; return PropDecoratorFactory; }); } const _global = globalThis; function ngDevModeResetPerfCounters() { const locationString = typeof location !== 'undefined' ? location.toString() : ''; const newCounters = { namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1, firstCreatePass: 0, tNode: 0, tView: 0, rendererCreateTextNode: 0, rendererSetText: 0, rendererCreateElement: 0, rendererAddEventListener: 0, rendererSetAttribute: 0, rendererRemoveAttribute: 0, rendererSetProperty: 0, rendererSetClassName: 0, rendererAddClass: 0, rendererRemoveClass: 0, rendererSetStyle: 0, rendererRemoveStyle: 0, rendererDestroy: 0, rendererDestroyNode: 0, rendererMoveNode: 0, rendererRemoveNode: 0, rendererAppendChild: 0, rendererInsertBefore: 0, rendererCreateComment: 0, hydratedNodes: 0, hydratedComponents: 0, dehydratedViewsRemoved: 0, dehydratedViewsCleanupRuns: 0, componentsSkippedHydration: 0, deferBlocksWithIncrementalHydration: 0, }; // Make sure to refer to ngDevMode as ['ngDevMode'] for closure. const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1; if (!allowNgDevModeTrue) { _global['ngDevMode'] = false; } else { if (typeof _global['ngDevMode'] !== 'object') { _global['ngDevMode'] = {}; } Object.assign(_global['ngDevMode'], newCounters); } return newCounters; } /** * This function checks to see if the `ngDevMode` has been set. If yes, * then we honor it, otherwise we default to dev mode with additional checks. * * The idea is that unless we are doing production build where we explicitly * set `ngDevMode == false` we should be helping the developer by providing * as much early warning and errors as possible. * * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode * is defined for the entire instruction set. * * When checking `ngDevMode` on toplevel, always init it before referencing it * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595. * * Details on possible values for `ngDevMode` can be found on its docstring. * * NOTE: * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`. */ function initNgDevMode() { // The below checks are to ensure that calling `initNgDevMode` multiple times does not // reset the counters. // If the `ngDevMode` is not an object, then it means we have not created the perf counters // yet. if (typeof ngDevMode === 'undefined' || ngDevMode) { if (typeof ngDevMode !== 'object' || Object.keys(ngDevMode).length === 0) { ngDevModeResetPerfCounters(); } return typeof ngDevMode !== 'undefined' && !!ngDevMode; } return false; } function getClosureSafeProperty(objWithPropertyToExtract) { for (let key in objWithPropertyToExtract) { if (objWithPropertyToExtract[key] === getClosureSafeProperty) { return key; } } throw Error('Could not find renamed property on target object.'); } /** * Sets properties on a target object from a source object, but only if * the property doesn't already exist on the target object. * @param target The target to set properties on * @param source The source of the property keys and values to set */ function fillProperties(target, source) { for (const key in source) { if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) { target[key] = source[key]; } } } function stringify(token) { if (typeof token === 'string') { return token; } if (Array.isArray(token)) { return `[${token.map(stringify).join(', ')}]`; } if (token == null) { return '' + token; } const name = token.overriddenName || token.name; if (name) { return `${name}`; } const result = token.toString(); if (result == null) { return '' + result; } const newLineIndex = result.indexOf('\n'); return newLineIndex >= 0 ? result.slice(0, newLineIndex) : result; } /** * Concatenates two strings with separator, allocating new strings only when necessary. * * @param before before string. * @param separator separator string. * @param after after string. * @returns concatenated string. */ function concatStringsWithSpace(before, after) { if (!before) return after || ''; if (!after) return before; return `${before} ${after}`; } /** * Ellipses the string in the middle when longer than the max length * * @param string * @param maxLength of the output string * @returns ellipsed string with ... in the middle */ function truncateMiddle(str, maxLength = 100) { if (!str || maxLength < 1 || str.length <= maxLength) return str; if (maxLength == 1) return str.substring(0, 1) + '...'; const halfLimit = Math.round(maxLength / 2); return str.substring(0, halfLimit) + '...' + str.substring(str.length - halfLimit); } const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty }); /** * Allows to refer to references which are not yet defined. * * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of * DI is declared, but not yet defined. It is also used when the `token` which we use when creating * a query is not yet defined. * * `forwardRef` is also used to break circularities in standalone components imports. * * @usageNotes * ### Circular dependency example * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'} * * ### Circular standalone reference import example * ```angular-ts * @Component({ * standalone: true, * imports: [ChildComponent], * selector: 'app-parent', * template: ``, * }) * export class ParentComponent { * @Input() hideParent: boolean; * } * * * @Component({ * standalone: true, * imports: [CommonModule, forwardRef(() => ParentComponent)], * selector: 'app-child', * template: ``, * }) * export class ChildComponent { * @Input() hideParent: boolean; * } * ``` * * @publicApi */ function forwardRef(forwardRefFn) { forwardRefFn.__forward_ref__ = forwardRef; forwardRefFn.toString = function () { return stringify(this()); }; return forwardRefFn; } /** * Lazily retrieves the reference value from a forwardRef. * * Acts as the identity function when given a non-forward-ref value. * * @usageNotes * ### Example * * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'} * * @see {@link forwardRef} * @publicApi */ function resolveForwardRef(type) { return isForwardRef(type) ? type() : type; } /** Checks whether a function is wrapped by a `forwardRef`. */ function isForwardRef(fn) { return (typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) && fn.__forward_ref__ === forwardRef); } // The functions in this file verify that the assumptions we are making // about state in an instruction are correct before implementing any logic. // They are meant only to be called in dev mode as sanity checks. function assertNumber(actual, msg) { if (!(typeof actual === 'number')) { throwError(msg, typeof actual, 'number', '==='); } } function assertNumberInRange(actual, minInclusive, maxInclusive) { assertNumber(actual, 'Expected a number'); assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to'); assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to'); } function assertString(actual, msg) { if (!(typeof actual === 'string')) { throwError(msg, actual === null ? 'null' : typeof actual, 'string', '==='); } } function assertFunction(actual, msg) { if (!(typeof actual === 'function')) { throwError(msg, actual === null ? 'null' : typeof actual, 'function', '==='); } } function assertEqual(actual, expected, msg) { if (!(actual == expected)) { throwError(msg, actual, expected, '=='); } } function assertNotEqual(actual, expected, msg) { if (!(actual != expected)) { throwError(msg, actual, expected, '!='); } } function assertSame(actual, expected, msg) { if (!(actual === expected)) { throwError(msg, actual, expected, '==='); } } function assertNotSame(actual, expected, msg) { if (!(actual !== expected)) { throwError(msg, actual, expected, '!=='); } } function assertLessThan(actual, expected, msg) { if (!(actual < expected)) { throwError(msg, actual, expected, '<'); } } function assertLessThanOrEqual(actual, expected, msg) { if (!(actual <= expected)) { throwError(msg, actual, expected, '<='); } } function assertGreaterThan(actual, expected, msg) { if (!(actual > expected)) { throwError(msg, actual, expected, '>'); } } function assertGreaterThanOrEqual(actual, expected, msg) { if (!(actual >= expected)) { throwError(msg, actual, expected, '>='); } } function assertDefined(actual, msg) { if (actual == null) { throwError(msg, actual, null, '!='); } } function throwError(msg, actual, expected, comparison) { throw new Error(`ASSERTION ERROR: ${msg}` + (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`)); } function assertDomNode(node) { if (!(node instanceof Node)) { throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`); } } function assertElement(node) { if (!(node instanceof Element)) { throwError(`The provided value must be an element but got ${stringify(node)}`); } } function assertIndexInRange(arr, index) { assertDefined(arr, 'Array must be defined.'); const maxLen = arr.length; if (index < 0 || index >= maxLen) { throwError(`Index expected to be less than ${maxLen} but got ${index}`); } } function assertOneOf(value, ...validValues) { if (validValues.indexOf(value) !== -1) return true; throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`); } function assertNotReactive(fn) { if (getActiveConsumer() !== null) { throwError(`${fn}() should never be called in a reactive context.`); } } /** * Construct an injectable definition which defines how a token will be constructed by the DI * system, and in which injectors (if any) it will be available. * * This should be assigned to a static `ɵprov` field on a type, which will then be an * `InjectableType`. * * Options: * * `providedIn` determines which injectors will include the injectable, by either associating it * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be * provided in the `'root'` injector, which will be the application-level injector in most apps. * * `factory` gives the zero argument function which will create an instance of the injectable. * The factory can call [`inject`](api/core/inject) to access the `Injector` and request injection * of dependencies. * * @codeGenApi * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm. */ function ɵɵdefineInjectable(opts) { return { token: opts.token, providedIn: opts.providedIn || null, factory: opts.factory, value: undefined, }; } /** * @deprecated in v8, delete after v10. This API should be used only by generated code, and that * code should now use ɵɵdefineInjectable instead. * @publicApi */ const defineInjectable = ɵɵdefineInjectable; /** * Construct an `InjectorDef` which configures an injector. * * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an * `InjectorType`. * * Options: * * * `providers`: an optional array of providers to add to the injector. Each provider must * either have a factory or point to a type which has a `ɵprov` static property (the * type must be an `InjectableType`). * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s * whose providers will also be added to the injector. Locally provided types will override * providers from imports. * * @codeGenApi */ function ɵɵdefineInjector(options) { return { providers: options.providers || [], imports: options.imports || [] }; } /** * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading * inherited value. * * @param type A type which may have its own (non-inherited) `ɵprov`. */ function getInjectableDef(type) { return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF); } function isInjectable(type) { return getInjectableDef(type) !== null; } /** * Return definition only if it is defined directly on `type` and is not inherited from a base * class of `type`. */ function getOwnDefinition(type, field) { return type.hasOwnProperty(field) ? type[field] : null; } /** * Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors. * * @param type A type which may have `ɵprov`, via inheritance. * * @deprecated Will be removed in a future version of Angular, where an error will occur in the * scenario if we find the `ɵprov` on an ancestor only. */ function getInheritedInjectableDef(type) { const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]); if (def) { ngDevMode && console.warn(`DEPRECATED: DI is instantiating a token "${type.name}" that inherits its @Injectable decorator but does not provide one itself.\n` + `This will become an error in a future version of Angular. Please add @Injectable() to the "${type.name}" class.`); return def; } else { return null; } } /** * Read the injector def type in a way which is immune to accidentally reading inherited value. * * @param type type which may have an injector def (`ɵinj`) */ function getInjectorDef(type) { return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ? type[NG_INJ_DEF] : null; } const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty }); const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty }); // We need to keep these around so we can read off old defs if new defs are unavailable const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty, }); const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty, }); /** * Creates a token that can be used in a DI Provider. * * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a * runtime representation) such as when injecting an interface, callable type, array or * parameterized type. * * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by * the `Injector`. This provides an additional level of type safety. * *
* * **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the * provider and the injection call. Creating a new instance of `InjectionToken` in different places, * even with the same description, will be treated as different tokens by Angular's DI system, * leading to a `NullInjectorError`. * *
* * {@example injection-token/src/main.ts region='InjectionToken'} * * When creating an `InjectionToken`, you can optionally specify a factory function which returns * (possibly by creating) a default value of the parameterized type `T`. This sets up the * `InjectionToken` using this factory as a provider as if it was defined explicitly in the * application's root injector. If the factory function, which takes zero arguments, needs to inject * dependencies, it can do so using the [`inject`](api/core/inject) function. * As you can see in the Tree-shakable InjectionToken example below. * * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which * overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note: * this option is now deprecated). As mentioned above, `'root'` is the default value for * `providedIn`. * * The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated. * * @usageNotes * ### Basic Examples * * ### Plain InjectionToken * * {@example core/di/ts/injector_spec.ts region='InjectionToken'} * * ### Tree-shakable InjectionToken * * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'} * * @publicApi */ class InjectionToken { _desc; /** @internal */ ngMetadataName = 'InjectionToken'; ɵprov; /** * @param _desc Description for the token, * used only for debugging purposes, * it should but does not need to be unique * @param options Options for the token's usage, as described above */ constructor(_desc, options) { this._desc = _desc; this.ɵprov = undefined; if (typeof options == 'number') { (typeof ngDevMode === 'undefined' || ngDevMode) && assertLessThan(options, 0, 'Only negative numbers are supported here'); // This is a special hack to assign __NG_ELEMENT_ID__ to this instance. // See `InjectorMarkers` this.__NG_ELEMENT_ID__ = options; } else if (options !== undefined) { this.ɵprov = ɵɵdefineInjectable({ token: this, providedIn: options.providedIn || 'root', factory: options.factory, }); } } /** * @internal */ get multi() { return this; } toString() { return `InjectionToken ${this._desc}`; } } let _injectorProfilerContext; function getInjectorProfilerContext() { !ngDevMode && throwError('getInjectorProfilerContext should never be called in production mode'); return _injectorProfilerContext; } function setInjectorProfilerContext(context) { !ngDevMode && throwError('setInjectorProfilerContext should never be called in production mode'); const previous = _injectorProfilerContext; _injectorProfilerContext = context; return previous; } let injectorProfilerCallback = null; /** * Sets the callback function which will be invoked during certain DI events within the * runtime (for example: injecting services, creating injectable instances, configuring providers) * * Warning: this function is *INTERNAL* and should not be relied upon in application's code. * The contract of the function might be changed in any release and/or the function can be removed * completely. * * @param profiler function provided by the caller or null value to disable profiling. */ const setInjectorProfiler = (injectorProfiler) => { !ngDevMode && throwError('setInjectorProfiler should never be called in production mode'); injectorProfilerCallback = injectorProfiler; }; /** * Injector profiler function which emits on DI events executed by the runtime. * * @param event InjectorProfilerEvent corresponding to the DI event being emitted */ function injectorProfiler(event) { !ngDevMode && throwError('Injector profiler should never be called in production mode'); if (injectorProfilerCallback != null /* both `null` and `undefined` */) { injectorProfilerCallback(event); } } /** * Emits an InjectorProfilerEventType.ProviderConfigured to the injector profiler. The data in the * emitted event includes the raw provider, as well as the token that provider is providing. * * @param eventProvider A provider object */ function emitProviderConfiguredEvent(eventProvider, isViewProvider = false) { !ngDevMode && throwError('Injector profiler should never be called in production mode'); let token; // if the provider is a TypeProvider (typeof provider is function) then the token is the // provider itself if (typeof eventProvider === 'function') { token = eventProvider; } // if the provider is an injection token, then the token is the injection token. else if (eventProvider instanceof InjectionToken) { token = eventProvider; } // in all other cases we can access the token via the `provide` property of the provider else { token = resolveForwardRef(eventProvider.provide); } let provider = eventProvider; // Injection tokens may define their own default provider which gets attached to the token itself // as `ɵprov`. In this case, we want to emit the provider that is attached to the token, not the // token itself. if (eventProvider instanceof InjectionToken) { provider = eventProvider.ɵprov || eventProvider; } injectorProfiler({ type: 2 /* InjectorProfilerEventType.ProviderConfigured */, context: getInjectorProfilerContext(), providerRecord: { token, provider, isViewProvider }, }); } /** * Emits an event to the injector profiler with the instance that was created. Note that * the injector associated with this emission can be accessed by using getDebugInjectContext() * * @param instance an object created by an injector */ function emitInstanceCreatedByInjectorEvent(instance) { !ngDevMode && throwError('Injector profiler should never be called in production mode'); injectorProfiler({ type: 1 /* InjectorProfilerEventType.InstanceCreatedByInjector */, context: getInjectorProfilerContext(), instance: { value: instance }, }); } /** * @param token DI token associated with injected service * @param value the instance of the injected service (i.e the result of `inject(token)`) * @param flags the flags that the token was injected with */ function emitInjectEvent(token, value, flags) { !ngDevMode && throwError('Injector profiler should never be called in production mode'); injectorProfiler({ type: 0 /* InjectorProfilerEventType.Inject */, context: getInjectorProfilerContext(), service: { token, value, flags }, }); } function emitEffectCreatedEvent(effect) { !ngDevMode && throwError('Injector profiler should never be called in production mode'); injectorProfiler({ type: 3 /* InjectorProfilerEventType.EffectCreated */, context: getInjectorProfilerContext(), effect, }); } function runInInjectorProfilerContext(injector, token, callback) { !ngDevMode && throwError('runInInjectorProfilerContext should never be called in production mode'); const prevInjectContext = setInjectorProfilerContext({ injector, token }); try { callback(); } finally { setInjectorProfilerContext(prevInjectContext); } } function isEnvironmentProviders(value) { return value && !!value.ɵproviders; } const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty }); const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty }); const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty }); const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty }); const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty }); /** * If a directive is diPublic, bloomAdd sets a property on the type with this constant as * the key and the directive's unique ID as the value. This allows us to map directives to their * bloom filter bit for DI. */ // TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified. const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty, }); /** * The `NG_ENV_ID` field on a DI token indicates special processing in the `EnvironmentInjector`: * getting such tokens from the `EnvironmentInjector` will bypass the standard DI resolution * strategy and instead will return implementation produced by the `NG_ENV_ID` factory function. * * This particular retrieval of DI tokens is mostly done to eliminate circular dependencies and * improve tree-shaking. */ const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty }); /** * Used for stringify render output in Ivy. * Important! This function is very performance-sensitive and we should * be extra careful not to introduce megamorphic reads in it. * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations. */ function renderStringify(value) { if (typeof value === 'string') return value; if (value == null) return ''; // Use `String` so that it invokes the `toString` method of the value. Note that this // appears to be faster than calling `value.toString` (see `render_stringify` benchmark). return String(value); } /** * Used to stringify a value so that it can be displayed in an error message. * * Important! This function contains a megamorphic read and should only be * used for error messages. */ function stringifyForError(value) { if (typeof value === 'function') return value.name || value.toString(); if (typeof value === 'object' && value != null && typeof value.type === 'function') { return value.type.name || value.type.toString(); } return renderStringify(value); } /** * Used to stringify a `Type` and including the file path and line number in which it is defined, if * possible, for better debugging experience. * * Important! This function contains a megamorphic read and should only be used for error messages. */ function debugStringifyTypeForError(type) { // TODO(pmvald): Do some refactoring so that we can use getComponentDef here without creating // circular deps. let componentDef = type[NG_COMP_DEF] || null; if (componentDef !== null && componentDef.debugInfo) { return stringifyTypeFromDebugInfo(componentDef.debugInfo); } return stringifyForError(type); } // TODO(pmvald): Do some refactoring so that we can use the type ClassDebugInfo for the param // debugInfo here without creating circular deps. function stringifyTypeFromDebugInfo(debugInfo) { if (!debugInfo.filePath || !debugInfo.lineNumber) { return debugInfo.className; } else { return `${debugInfo.className} (at ${debugInfo.filePath}:${debugInfo.lineNumber})`; } } /** Called when directives inject each other (creating a circular dependency) */ function throwCyclicDependencyError(token, path) { throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, ngDevMode ? `Circular dependency in DI detected for ${token}${path ? `. Dependency path: ${path.join(' > ')} > ${token}` : ''}` : token); } function throwMixedMultiProviderError() { throw new Error(`Cannot mix multi providers and regular providers`); } function throwInvalidProviderError(ngModuleType, providers, provider) { if (ngModuleType && providers) { const providerDetail = providers.map((v) => (v == provider ? '?' + provider + '?' : '...')); throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`); } else if (isEnvironmentProviders(provider)) { if (provider.ɵfromNgModule) { throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`); } else { throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers present in a non-environment injector. 'EnvironmentProviders' can't be used for component providers.`); } } else { throw new Error('Invalid provider'); } } /** Throws an error when a token is not found in DI. */ function throwProviderNotFoundError(token, injectorName) { const errorMessage = ngDevMode && `No provider for ${stringifyForError(token)} found${injectorName ? ` in ${injectorName}` : ''}`; throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, errorMessage); } /** * Injection flags for DI. * * @publicApi * @deprecated use an options object for [`inject`](api/core/inject) instead. */ var InjectFlags; (function (InjectFlags) { // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer // writes exports of it into ngfactory files. /** Check self and check parent injector if needed */ InjectFlags[InjectFlags["Default"] = 0] = "Default"; /** * Specifies that an injector should retrieve a dependency from any injector until reaching the * host element of the current component. (Only used with Element Injector) */ InjectFlags[InjectFlags["Host"] = 1] = "Host"; /** Don't ascend to ancestors of the node requesting injection. */ InjectFlags[InjectFlags["Self"] = 2] = "Self"; /** Skip the node that is requesting injection. */ InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf"; /** Inject `defaultValue` instead if token not found. */ InjectFlags[InjectFlags["Optional"] = 8] = "Optional"; })(InjectFlags || (InjectFlags = {})); /** * Current implementation of inject. * * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this * way for two reasons: * 1. `Injector` should not depend on ivy logic. * 2. To maintain tree shake-ability we don't want to bring in unnecessary code. */ let _injectImplementation; function getInjectImplementation() { return _injectImplementation; } /** * Sets the current inject implementation. */ function setInjectImplementation(impl) { const previous = _injectImplementation; _injectImplementation = impl; return previous; } /** * Injects `root` tokens in limp mode. * * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to * `"root"`. This is known as the limp mode injection. In such case the value is stored in the * injectable definition. */ function injectRootLimpMode(token, notFoundValue, flags) { const injectableDef = getInjectableDef(token); if (injectableDef && injectableDef.providedIn == 'root') { return injectableDef.value === undefined ? (injectableDef.value = injectableDef.factory()) : injectableDef.value; } if (flags & InjectFlags.Optional) return null; if (notFoundValue !== undefined) return notFoundValue; throwProviderNotFoundError(token, 'Injector'); } /** * Assert that `_injectImplementation` is not `fn`. * * This is useful, to prevent infinite recursion. * * @param fn Function which it should not equal to */ function assertInjectImplementationNotEqual(fn) { ngDevMode && assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion'); } const _THROW_IF_NOT_FOUND = {}; const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND; /* * Name of a property (that we patch onto DI decorator), which is used as an annotation of which * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators * in the code, thus making them tree-shakable. */ const DI_DECORATOR_FLAG = '__NG_DI_FLAG__'; class RetrievingInjector { injector; constructor(injector) { this.injector = injector; } retrieve(token, options) { const ngOptions = options; return this.injector.get(token, ngOptions.optional ? NOT_FOUND$1 : THROW_IF_NOT_FOUND, ngOptions); } } const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath'; const NG_TOKEN_PATH = 'ngTokenPath'; const NEW_LINE = /\n/gm; const NO_NEW_LINE = 'ɵ'; const SOURCE = '__source'; function injectInjectorOnly(token, flags = InjectFlags.Default) { if (getCurrentInjector() === undefined) { throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode && `The \`${stringify(token)}\` token injection failed. \`inject()\` function must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`runInInjectionContext\`.`); } else if (getCurrentInjector() === null) { return injectRootLimpMode(token, undefined, flags); } else { const currentInjector = getCurrentInjector(); let injector; if (currentInjector instanceof RetrievingInjector) { injector = currentInjector.injector; } else { injector = currentInjector; } const value = injector.get(token, flags & InjectFlags.Optional ? null : undefined, flags); ngDevMode && emitInjectEvent(token, value, flags); return value; } } function ɵɵinject(token, flags = InjectFlags.Default) { return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags); } /** * Throws an error indicating that a factory function could not be generated by the compiler for a * particular class. * * The name of the class is not mentioned here, but will be in the generated factory function name * and thus in the stack trace. * * @codeGenApi */ function ɵɵinvalidFactoryDep(index) { throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode && `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid. This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator. 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.`); } /** * Injects a token from the currently active injector. * `inject` is only supported in an [injection context](guide/di/dependency-injection-context). It * can be used during: * - Construction (via the `constructor`) of a class being instantiated by the DI system, such * as an `@Injectable` or `@Component`. * - In the initializer for fields of such classes. * - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`. * - In the `factory` function specified for an `InjectionToken`. * - In a stackframe of a function call in a DI context * * @param token A token that represents a dependency that should be injected. * @param flags Optional flags that control how injection is executed. * The flags correspond to injection strategies that can be specified with * parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`. * @returns the injected value if operation is successful, `null` otherwise. * @throws if called outside of a supported context. * * @usageNotes * In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a * field initializer: * * ```ts * @Injectable({providedIn: 'root'}) * export class Car { * radio: Radio|undefined; * // OK: field initializer * spareTyre = inject(Tyre); * * constructor() { * // OK: constructor body * this.radio = inject(Radio); * } * } * ``` * * It is also legal to call `inject` from a provider's factory: * * ```ts * providers: [ * {provide: Car, useFactory: () => { * // OK: a class factory * const engine = inject(Engine); * return new Car(engine); * }} * ] * ``` * * Calls to the `inject()` function outside of the class creation context will result in error. Most * notably, calls to `inject()` are disallowed after a class instance was created, in methods * (including lifecycle hooks): * * ```ts * @Component({ ... }) * export class CarComponent { * ngOnInit() { * // ERROR: too late, the component instance was already created * const engine = inject(Engine); * engine.start(); * } * } * ``` * * @publicApi */ function inject(token, flags = InjectFlags.Default) { // The `as any` here _shouldn't_ be necessary, but without it JSCompiler // throws a disambiguation error due to the multiple signatures. return ɵɵinject(token, convertToBitFlags(flags)); } // Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`). function convertToBitFlags(flags) { 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 (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter (flags.optional && 8 /* InternalInjectFlags.Optional */) | (flags.host && 1 /* InternalInjectFlags.Host */) | (flags.self && 2 /* InternalInjectFlags.Self */) | (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */)); } function injectArgs(types) { const args = []; for (let i = 0; i < types.length; i++) { const arg = resolveForwardRef(types[i]); if (Array.isArray(arg)) { if (arg.length === 0) { throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.'); } let type = undefined; let flags = InjectFlags.Default; for (let j = 0; j < arg.length; j++) { const meta = arg[j]; const flag = getInjectFlag(meta); if (typeof flag === 'number') { // Special case when we handle @Inject decorator. if (flag === -1 /* DecoratorFlags.Inject */) { type = meta.token; } else { flags |= flag; } } else { type = meta; } } args.push(ɵɵinject(type, flags)); } else { args.push(ɵɵinject(arg)); } } return args; } /** * Attaches a given InjectFlag to a given decorator using monkey-patching. * Since DI decorators can be used in providers `deps` array (when provider is configured using * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we * attach the flag to make it available both as a static property and as a field on decorator * instance. * * @param decorator Provided DI decorator. * @param flag InjectFlag that should be applied. */ function attachInjectFlag(decorator, flag) { decorator[DI_DECORATOR_FLAG] = flag; decorator.prototype[DI_DECORATOR_FLAG] = flag; return decorator; } /** * Reads monkey-patched property that contains InjectFlag attached to a decorator. * * @param token Token that may contain monkey-patched DI flags property. */ function getInjectFlag(token) { return token[DI_DECORATOR_FLAG]; } function catchInjectorError(e, token, injectorErrorName, source) { const tokenPath = e[NG_TEMP_TOKEN_PATH]; if (token[SOURCE]) { tokenPath.unshift(token[SOURCE]); } e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source); e[NG_TOKEN_PATH] = tokenPath; e[NG_TEMP_TOKEN_PATH] = null; throw e; } function formatError(text, obj, injectorErrorName, source = null) { text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.slice(2) : text; let context = stringify(obj); if (Array.isArray(obj)) { context = obj.map(stringify).join(' -> '); } else if (typeof obj === 'object') { let parts = []; for (let key in obj) { if (obj.hasOwnProperty(key)) { let value = obj[key]; parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value))); } } context = `{${parts.join(', ')}}`; } return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`; } /** * Inject decorator and metadata. * * @Annotation * @publicApi */ const Inject = attachInjectFlag( // Disable tslint because `DecoratorFlags` is a const enum which gets inlined. makeParamDecorator('Inject', (token) => ({ token })), -1 /* DecoratorFlags.Inject */); /** * Optional decorator and metadata. * * @Annotation * @publicApi */ const Optional = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Optional'), 8 /* InternalInjectFlags.Optional */); /** * Self decorator and metadata. * * @Annotation * @publicApi */ const Self = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Self'), 2 /* InternalInjectFlags.Self */); /** * `SkipSelf` decorator and metadata. * * @Annotation * @publicApi */ const SkipSelf = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* InternalInjectFlags.SkipSelf */); /** * Host decorator and metadata. * * @Annotation * @publicApi */ const Host = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Host'), 1 /* InternalInjectFlags.Host */); function getFactoryDef(type, throwNotFound) { const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF); if (!hasFactoryDef && throwNotFound === true && ngDevMode) { throw new Error(`Type ${stringify(type)} does not have 'ɵfac' property.`); } return hasFactoryDef ? type[NG_FACTORY_DEF] : null; } /** * Determines if the contents of two arrays is identical * * @param a first array * @param b second array * @param identityAccessor Optional function for extracting stable object identity from a value in * the array. */ function arrayEquals(a, b, identityAccessor) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { let valueA = a[i]; let valueB = b[i]; if (identityAccessor) { valueA = identityAccessor(valueA); valueB = identityAccessor(valueB); } if (valueB !== valueA) { return false; } } return true; } /** * Flattens an array. */ function flatten(list) { return list.flat(Number.POSITIVE_INFINITY); } function deepForEach(input, fn) { input.forEach((value) => (Array.isArray(value) ? deepForEach(value, fn) : fn(value))); } function addToArray(arr, index, value) { // perf: array.push is faster than array.splice! if (index >= arr.length) { arr.push(value); } else { arr.splice(index, 0, value); } } function removeFromArray(arr, index) { // perf: array.pop is faster than array.splice! if (index >= arr.length - 1) { return arr.pop(); } else { return arr.splice(index, 1)[0]; } } function newArray(size, value) { const list = []; for (let i = 0; i < size; i++) { list.push(value); } return list; } /** * Remove item from array (Same as `Array.splice()` but faster.) * * `Array.splice()` is not as fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * https://jsperf.com/fast-array-splice (About 20x faster) * * @param array Array to splice * @param index Index of element in array to remove. * @param count Number of items to remove. */ function arraySplice(array, index, count) { const length = array.length - count; while (index < length) { array[index] = array[index + count]; index++; } while (count--) { array.pop(); // shrink the array } } /** * Same as `Array.splice2(index, 0, value1, value2)` but faster. * * `Array.splice()` is not fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * @param array Array to splice. * @param index Index in array where the `value` should be added. * @param value1 Value to add to array. * @param value2 Value to add to array. */ function arrayInsert2(array, index, value1, value2) { ngDevMode && assertLessThanOrEqual(index, array.length, "Can't insert past array end."); let end = array.length; if (end == index) { // inserting at the end. array.push(value1, value2); } else if (end === 1) { // corner case when we have less items in array than we have items to insert. array.push(value2, array[0]); array[0] = value1; } else { end--; array.push(array[end - 1], array[end]); while (end > index) { const previousEnd = end - 2; array[end] = array[previousEnd]; end--; } array[index] = value1; array[index + 1] = value2; } } /** * Set a `value` for a `key`. * * @param keyValueArray to modify. * @param key The key to locate or create. * @param value The value to set for a `key`. * @returns index (always even) of where the value vas set. */ function keyValueArraySet(keyValueArray, key, value) { let index = keyValueArrayIndexOf(keyValueArray, key); if (index >= 0) { // if we found it set it. keyValueArray[index | 1] = value; } else { index = ~index; arrayInsert2(keyValueArray, index, key, value); } return index; } /** * Retrieve a `value` for a `key` (on `undefined` if not found.) * * @param keyValueArray to search. * @param key The key to locate. * @return The `value` stored at the `key` location or `undefined if not found. */ function keyValueArrayGet(keyValueArray, key) { const index = keyValueArrayIndexOf(keyValueArray, key); if (index >= 0) { // if we found it retrieve it. return keyValueArray[index | 1]; } return undefined; } /** * Retrieve a `key` index value in the array or `-1` if not found. * * @param keyValueArray to search. * @param key The key to locate. * @returns index of where the key is (or should have been.) * - positive (even) index if key found. * - negative index if key not found. (`~index` (even) to get the index where it should have * been inserted.) */ function keyValueArrayIndexOf(keyValueArray, key) { return _arrayIndexOfSorted(keyValueArray, key, 1); } /** * INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`. * * NOTE: * - This uses binary search algorithm for fast removals. * * @param array A sorted array to binary search. * @param value The value to look for. * @param shift grouping shift. * - `0` means look at every location * - `1` means only look at every other (even) location (the odd locations are to be ignored as * they are values.) * @returns index of the value. * - positive index if value found. * - negative index if value not found. (`~index` to get the value where it should have been * inserted) */ function _arrayIndexOfSorted(array, value, shift) { ngDevMode && assertEqual(Array.isArray(array), true, 'Expecting an array'); let start = 0; let end = array.length >> shift; while (end !== start) { const middle = start + ((end - start) >> 1); // find the middle. const current = array[middle << shift]; if (value === current) { return middle << shift; } else if (current > value) { end = middle; } else { start = middle + 1; // We already searched middle so make it non-inclusive by adding 1 } } return ~(end << shift); } /** * This file contains reuseable "empty" symbols that can be used as default return values * in different parts of the rendering code. Because the same symbols are returned, this * allows for identity checks against these values to be consistently used by the framework * code. */ const EMPTY_OBJ = {}; const EMPTY_ARRAY = []; // freezing the values prevents any code from accidentally inserting new values in if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) { // These property accesses can be ignored because ngDevMode will be set to false // when optimizing code and the whole if statement will be dropped. // tslint:disable-next-line:no-toplevel-property-access Object.freeze(EMPTY_OBJ); // tslint:disable-next-line:no-toplevel-property-access Object.freeze(EMPTY_ARRAY); } /** * A multi-provider token for initialization functions that will run upon construction of an * environment injector. * * @deprecated from v19.0.0, use provideEnvironmentInitializer instead * * @see {@link provideEnvironmentInitializer} * * Note: As opposed to the `APP_INITIALIZER` token, the `ENVIRONMENT_INITIALIZER` functions are not awaited, * hence they should not be `async`. * * @publicApi */ const ENVIRONMENT_INITIALIZER = new InjectionToken(ngDevMode ? 'ENVIRONMENT_INITIALIZER' : ''); /** * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors. * * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a * project. * * @publicApi */ const INJECTOR$1 = new InjectionToken(ngDevMode ? 'INJECTOR' : '', // Disable tslint because this is const enum which gets inlined not top level prop access. // tslint:disable-next-line: no-toplevel-property-access -1 /* InjectorMarkers.Injector */); const INJECTOR_DEF_TYPES = new InjectionToken(ngDevMode ? 'INJECTOR_DEF_TYPES' : ''); class NullInjector { get(token, notFoundValue = THROW_IF_NOT_FOUND) { if (notFoundValue === THROW_IF_NOT_FOUND) { const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`); error.name = 'NullInjectorError'; throw error; } return notFoundValue; } } function getNgModuleDef(type, throwNotFound) { const ngModuleDef = type[NG_MOD_DEF] || null; if (!ngModuleDef && throwNotFound === true) { throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`); } return ngModuleDef; } /** * The following getter methods retrieve the definition from the type. Currently the retrieval * honors inheritance, but in the future we may change the rule to require that definitions are * explicit. This would require some sort of migration strategy. */ function getComponentDef(type) { return type[NG_COMP_DEF] || null; } function getDirectiveDef(type) { return type[NG_DIR_DEF] || null; } function getPipeDef$1(type) { return type[NG_PIPE_DEF] || null; } /** * Checks whether a given Component, Directive or Pipe is marked as standalone. * This will return false if passed anything other than a Component, Directive, or Pipe class * See [this guide](guide/components/importing) for additional information: * * @param type A reference to a Component, Directive or Pipe. * @publicApi */ function isStandalone(type) { const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type); return def !== null && def.standalone; } /** * Wrap an array of `Provider`s into `EnvironmentProviders`, preventing them from being accidentally * referenced in `@Component` in a component injector. * * @publicApi */ function makeEnvironmentProviders(providers) { return { ɵproviders: providers, }; } /** * @description * This function is used to provide initialization functions that will be executed upon construction * of an environment injector. * * Note that the provided initializer is run in the injection context. * * Previously, this was achieved using the `ENVIRONMENT_INITIALIZER` token which is now deprecated. * * @see {@link ENVIRONMENT_INITIALIZER} * * @usageNotes * The following example illustrates how to configure an initialization function using * `provideEnvironmentInitializer()` * ```ts * createEnvironmentInjector( * [ * provideEnvironmentInitializer(() => { * console.log('environment initialized'); * }), * ], * parentInjector * ); * ``` * * @publicApi */ function provideEnvironmentInitializer(initializerFn) { return makeEnvironmentProviders([ { provide: ENVIRONMENT_INITIALIZER, multi: true, useValue: initializerFn, }, ]); } /** * Collects providers from all NgModules and standalone components, including transitively imported * ones. * * Providers extracted via `importProvidersFrom` are only usable in an application injector or * another environment injector (such as a route injector). They should not be used in component * providers. * * More information about standalone components can be found in [this * guide](guide/components/importing). * * @usageNotes * The results of the `importProvidersFrom` call can be used in the `bootstrapApplication` call: * * ```ts * await bootstrapApplication(RootComponent, { * providers: [ * importProvidersFrom(NgModuleOne, NgModuleTwo) * ] * }); * ``` * * You can also use the `importProvidersFrom` results in the `providers` field of a route, when a * standalone component is used: * * ```ts * export const ROUTES: Route[] = [ * { * path: 'foo', * providers: [ * importProvidersFrom(NgModuleOne, NgModuleTwo) * ], * component: YourStandaloneComponent * } * ]; * ``` * * @returns Collected providers from the specified list of types. * @publicApi */ function importProvidersFrom(...sources) { return { ɵproviders: internalImportProvidersFrom(true, sources), ɵfromNgModule: true, }; } function internalImportProvidersFrom(checkForStandaloneCmp, ...sources) { const providersOut = []; const dedup = new Set(); // already seen types let injectorTypesWithProviders; const collectProviders = (provider) => { providersOut.push(provider); }; deepForEach(sources, (source) => { if ((typeof ngDevMode === 'undefined' || ngDevMode) && checkForStandaloneCmp) { const cmpDef = getComponentDef(source); if (cmpDef?.standalone) { throw new RuntimeError(800 /* RuntimeErrorCode.IMPORT_PROVIDERS_FROM_STANDALONE */, `Importing providers supports NgModule or ModuleWithProviders but got a standalone component "${stringifyForError(source)}"`); } } // Narrow `source` to access the internal type analogue for `ModuleWithProviders`. const internalSource = source; if (walkProviderTree(internalSource, collectProviders, [], dedup)) { injectorTypesWithProviders ||= []; injectorTypesWithProviders.push(internalSource); } }); // Collect all providers from `ModuleWithProviders` types. if (injectorTypesWithProviders !== undefined) { processInjectorTypesWithProviders(injectorTypesWithProviders, collectProviders); } return providersOut; } /** * Collects all providers from the list of `ModuleWithProviders` and appends them to the provided * array. */ function processInjectorTypesWithProviders(typesWithProviders, visitor) { for (let i = 0; i < typesWithProviders.length; i++) { const { ngModule, providers } = typesWithProviders[i]; deepForEachProvider(providers, (provider) => { ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule); visitor(provider, ngModule); }); } } /** * The logic visits an `InjectorType`, an `InjectorTypeWithProviders`, or a standalone * `ComponentType`, and all of its transitive providers and collects providers. * * If an `InjectorTypeWithProviders` that declares providers besides the type is specified, * the function will return "true" to indicate that the providers of the type definition need * to be processed. This allows us to process providers of injector types after all imports of * an injector definition are processed. (following View Engine semantics: see FW-1349) */ function walkProviderTree(container, visitor, parents, dedup) { container = resolveForwardRef(container); if (!container) return false; // The actual type which had the definition. Usually `container`, but may be an unwrapped type // from `InjectorTypeWithProviders`. let defType = null; let injDef = getInjectorDef(container); const cmpDef = !injDef && getComponentDef(container); if (!injDef && !cmpDef) { // `container` is not an injector type or a component type. It might be: // * An `InjectorTypeWithProviders` that wraps an injector type. // * A standalone directive or pipe that got pulled in from a standalone component's // dependencies. // Try to unwrap it as an `InjectorTypeWithProviders` first. const ngModule = container .ngModule; injDef = getInjectorDef(ngModule); if (injDef) { defType = ngModule; } else { // Not a component or injector type, so ignore it. return false; } } else if (cmpDef && !cmpDef.standalone) { return false; } else { defType = container; } // Check for circular dependencies. if (ngDevMode && parents.indexOf(defType) !== -1) { const defName = stringify(defType); const path = parents.map(stringify); throwCyclicDependencyError(defName, path); } // Check for multiple imports of the same module const isDuplicate = dedup.has(defType); if (cmpDef) { if (isDuplicate) { // This component definition has already been processed. return false; } dedup.add(defType); if (cmpDef.dependencies) { const deps = typeof cmpDef.dependencies === 'function' ? cmpDef.dependencies() : cmpDef.dependencies; for (const dep of deps) { walkProviderTree(dep, visitor, parents, dedup); } } } else if (injDef) { // First, include providers from any imports. if (injDef.imports != null && !isDuplicate) { // Before processing defType's imports, add it to the set of parents. This way, if it ends // up deeply importing itself, this can be detected. ngDevMode && parents.push(defType); // Add it to the set of dedups. This way we can detect multiple imports of the same module dedup.add(defType); let importTypesWithProviders; try { deepForEach(injDef.imports, (imported) => { if (walkProviderTree(imported, visitor, parents, dedup)) { importTypesWithProviders ||= []; // If the processed import is an injector type with providers, we store it in the // list of import types with providers, so that we can process those afterwards. importTypesWithProviders.push(imported); } }); } finally { // Remove it from the parents set when finished. ngDevMode && parents.pop(); } // Imports which are declared with providers (TypeWithProviders) need to be processed // after all imported modules are processed. This is similar to how View Engine // processes/merges module imports in the metadata resolver. See: FW-1349. if (importTypesWithProviders !== undefined) { processInjectorTypesWithProviders(importTypesWithProviders, visitor); } } if (!isDuplicate) { // Track the InjectorType and add a provider for it. // It's important that this is done after the def's imports. const factory = getFactoryDef(defType) || (() => new defType()); // Append extra providers to make more info available for consumers (to retrieve an injector // type), as well as internally (to calculate an injection scope correctly and eagerly // instantiate a `defType` when an injector is created). // Provider to create `defType` using its factory. visitor({ provide: defType, useFactory: factory, deps: EMPTY_ARRAY }, defType); // Make this `defType` available to an internal logic that calculates injector scope. visitor({ provide: INJECTOR_DEF_TYPES, useValue: defType, multi: true }, defType); // Provider to eagerly instantiate `defType` via `INJECTOR_INITIALIZER`. visitor({ provide: ENVIRONMENT_INITIALIZER, useValue: () => ɵɵinject(defType), multi: true }, defType); } // Next, include providers listed on the definition itself. const defProviders = injDef.providers; if (defProviders != null && !isDuplicate) { const injectorType = container; deepForEachProvider(defProviders, (provider) => { ngDevMode && validateProvider(provider, defProviders, injectorType); visitor(provider, injectorType); }); } } else { // Should not happen, but just in case. return false; } return (defType !== container && container.providers !== undefined); } function validateProvider(provider, providers, containerType) { if (isTypeProvider(provider) || isValueProvider(provider) || isFactoryProvider(provider) || isExistingProvider(provider)) { return; } // Here we expect the provider to be a `useClass` provider (by elimination). const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide)); if (!classRef) { throwInvalidProviderError(containerType, providers, provider); } } function deepForEachProvider(providers, fn) { for (let provider of providers) { if (isEnvironmentProviders(provider)) { provider = provider.ɵproviders; } if (Array.isArray(provider)) { deepForEachProvider(provider, fn); } else { fn(provider); } } } const USE_VALUE$1 = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty, }); function isValueProvider(value) { return value !== null && typeof value == 'object' && USE_VALUE$1 in value; } function isExistingProvider(value) { return !!(value && value.useExisting); } function isFactoryProvider(value) { return !!(value && value.useFactory); } function isTypeProvider(value) { return typeof value === 'function'; } function isClassProvider(value) { return !!value.useClass; } /** * An internal token whose presence in an injector indicates that the injector should treat itself * as a root scoped injector when processing requests for unknown tokens which may indicate * they are provided in the root scope. */ const INJECTOR_SCOPE = new InjectionToken(ngDevMode ? 'Set Injector scope.' : ''); /** * Marker which indicates that a value has not yet been created from the factory function. */ const NOT_YET = {}; /** * Marker which indicates that the factory function for a token is in the process of being called. * * If the injector is asked to inject a token with its value set to CIRCULAR, that indicates * injection of a dependency has recursively attempted to inject the original token, and there is * a circular dependency among the providers. */ const CIRCULAR = {}; /** * A lazily initialized NullInjector. */ let NULL_INJECTOR = undefined; function getNullInjector() { if (NULL_INJECTOR === undefined) { NULL_INJECTOR = new NullInjector(); } return NULL_INJECTOR; } /** * An `Injector` that's part of the environment injector hierarchy, which exists outside of the * component tree. */ class EnvironmentInjector { } class R3Injector extends EnvironmentInjector { parent; source; scopes; /** * Map of tokens to records which contain the instances of those tokens. * - `null` value implies that we don't have the record. Used by tree-shakable injectors * to prevent further searches. */ records = new Map(); /** * Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks. */ _ngOnDestroyHooks = new Set(); _onDestroyHooks = []; /** * Flag indicating that this injector was previously destroyed. */ get destroyed() { return this._destroyed; } _destroyed = false; injectorDefTypes; constructor(providers, parent, source, scopes) { super(); this.parent = parent; this.source = source; this.scopes = scopes; // Start off by creating Records for every provider. forEachSingleProvider(providers, (provider) => this.processProvider(provider)); // Make sure the INJECTOR token provides this injector. this.records.set(INJECTOR$1, makeRecord(undefined, this)); // And `EnvironmentInjector` if the current injector is supposed to be env-scoped. if (scopes.has('environment')) { this.records.set(EnvironmentInjector, makeRecord(undefined, this)); } // Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide // any injectable scoped to APP_ROOT_SCOPE. const record = this.records.get(INJECTOR_SCOPE); if (record != null && typeof record.value === 'string') { this.scopes.add(record.value); } this.injectorDefTypes = new Set(this.get(INJECTOR_DEF_TYPES, EMPTY_ARRAY, InjectFlags.Self)); } retrieve(token, options) { const ngOptions = options; return this.get(token, ngOptions.optional ? NOT_FOUND$2 : THROW_IF_NOT_FOUND, ngOptions); } /** * Destroy the injector and release references to every instance or provider associated with it. * * Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a * hook was found. */ destroy() { assertNotDestroyed(this); // Set destroyed = true first, in case lifecycle hooks re-enter destroy(). this._destroyed = true; const prevConsumer = setActiveConsumer(null); try { // Call all the lifecycle hooks. for (const service of this._ngOnDestroyHooks) { service.ngOnDestroy(); } const onDestroyHooks = this._onDestroyHooks; // Reset the _onDestroyHooks array before iterating over it to prevent hooks that unregister // themselves from mutating the array during iteration. this._onDestroyHooks = []; for (const hook of onDestroyHooks) { hook(); } } finally { // Release all references. this.records.clear(); this._ngOnDestroyHooks.clear(); this.injectorDefTypes.clear(); setActiveConsumer(prevConsumer); } } onDestroy(callback) { assertNotDestroyed(this); this._onDestroyHooks.push(callback); return () => this.removeOnDestroy(callback); } runInContext(fn) { assertNotDestroyed(this); const previousInjector = setCurrentInjector(this); const previousInjectImplementation = setInjectImplementation(undefined); let prevInjectContext; if (ngDevMode) { prevInjectContext = setInjectorProfilerContext({ injector: this, token: null }); } try { return fn(); } finally { setCurrentInjector(previousInjector); setInjectImplementation(previousInjectImplementation); ngDevMode && setInjectorProfilerContext(prevInjectContext); } } get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) { assertNotDestroyed(this); if (token.hasOwnProperty(NG_ENV_ID)) { return token[NG_ENV_ID](this); } flags = convertToBitFlags(flags); // Set the injection context. let prevInjectContext; if (ngDevMode) { prevInjectContext = setInjectorProfilerContext({ injector: this, token: token }); } const previousInjector = setCurrentInjector(this); const previousInjectImplementation = setInjectImplementation(undefined); try { // Check for the SkipSelf flag. if (!(flags & InjectFlags.SkipSelf)) { // SkipSelf isn't set, check if the record belongs to this injector. let record = this.records.get(token); if (record === undefined) { // No record, but maybe the token is scoped to this injector. Look for an injectable // def with a scope matching this injector. const def = couldBeInjectableType(token) && getInjectableDef(token); if (def && this.injectableDefInScope(def)) { // Found an injectable def and it's scoped to this injector. Pretend as if it was here // all along. if (ngDevMode) { runInInjectorProfilerContext(this, token, () => { emitProviderConfiguredEvent(token); }); } record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET); } else { record = null; } this.records.set(token, record); } // If a record was found, get the instance for it and return it. if (record != null /* NOT null || undefined */) { return this.hydrate(token, record, flags); } } // 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(); // 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 = (e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || []); path.unshift(stringify(token)); if (previousInjector) { // We still have a parent injector, keep throwing throw e; } else { // Format & throw the final error message when we don't have any previous injector return catchInjectorError(e, token, 'R3InjectorError', this.source); } } else { throw e; } } finally { // Lastly, restore the previous injection context. setInjectImplementation(previousInjectImplementation); setCurrentInjector(previousInjector); ngDevMode && setInjectorProfilerContext(prevInjectContext); } } /** @internal */ resolveInjectorInitializers() { const prevConsumer = setActiveConsumer(null); const previousInjector = setCurrentInjector(this); const previousInjectImplementation = setInjectImplementation(undefined); let prevInjectContext; if (ngDevMode) { prevInjectContext = setInjectorProfilerContext({ injector: this, token: null }); } try { const initializers = this.get(ENVIRONMENT_INITIALIZER, EMPTY_ARRAY, InjectFlags.Self); if (ngDevMode && !Array.isArray(initializers)) { throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' + `(expected an array, but got ${typeof initializers}). ` + 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' + '`multi: true` provider.'); } for (const initializer of initializers) { initializer(); } } finally { setCurrentInjector(previousInjector); setInjectImplementation(previousInjectImplementation); ngDevMode && setInjectorProfilerContext(prevInjectContext); setActiveConsumer(prevConsumer); } } toString() { const tokens = []; const records = this.records; for (const token of records.keys()) { tokens.push(stringify(token)); } return `R3Injector[${tokens.join(', ')}]`; } /** * Process a `SingleProvider` and add it. */ processProvider(provider) { // Determine the token from the provider. Either it's its own token, or has a {provide: ...} // property. provider = resolveForwardRef(provider); let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider && provider.provide); // Construct a `Record` for the provider. const record = providerToRecord(provider); if (ngDevMode) { runInInjectorProfilerContext(this, token, () => { // Emit InjectorProfilerEventType.Create if provider is a value provider because // these are the only providers that do not go through the value hydration logic // where this event would normally be emitted from. if (isValueProvider(provider)) { emitInstanceCreatedByInjectorEvent(provider.useValue); } emitProviderConfiguredEvent(provider); }); } if (!isTypeProvider(provider) && provider.multi === true) { // If the provider indicates that it's a multi-provider, process it specially. // First check whether it's been defined already. let multiRecord = this.records.get(token); if (multiRecord) { // It has. Throw a nice error if if (ngDevMode && multiRecord.multi === undefined) { throwMixedMultiProviderError(); } } else { multiRecord = makeRecord(undefined, NOT_YET, true); multiRecord.factory = () => injectArgs(multiRecord.multi); this.records.set(token, multiRecord); } token = provider; multiRecord.multi.push(provider); } else { if (ngDevMode) { const existing = this.records.get(token); if (existing && existing.multi !== undefined) { throwMixedMultiProviderError(); } } } this.records.set(token, record); } hydrate(token, record, flags) { const prevConsumer = setActiveConsumer(null); try { if (record.value === CIRCULAR) { throwCyclicDependencyError(stringify(token)); } else if (record.value === NOT_YET) { record.value = CIRCULAR; if (ngDevMode) { runInInjectorProfilerContext(this, token, () => { record.value = record.factory(undefined, flags); emitInstanceCreatedByInjectorEvent(record.value); }); } else { record.value = record.factory(undefined, flags); } } if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) { this._ngOnDestroyHooks.add(record.value); } return record.value; } finally { setActiveConsumer(prevConsumer); } } injectableDefInScope(def) { if (!def.providedIn) { return false; } const providedIn = resolveForwardRef(def.providedIn); if (typeof providedIn === 'string') { return providedIn === 'any' || this.scopes.has(providedIn); } else { return this.injectorDefTypes.has(providedIn); } } removeOnDestroy(callback) { const destroyCBIdx = this._onDestroyHooks.indexOf(callback); if (destroyCBIdx !== -1) { this._onDestroyHooks.splice(destroyCBIdx, 1); } } } function injectableDefOrInjectorDefFactory(token) { // Most tokens will have an injectable def directly on them, which specifies a factory directly. const injectableDef = getInjectableDef(token); const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token); if (factory !== null) { return factory; } // InjectionTokens should have an injectable def (ɵprov) and thus should be handled above. // If it's missing that, it's an error. if (token instanceof InjectionToken) { throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Token ${stringify(token)} is missing a ɵprov definition.`); } // Undecorated types can sometimes be created if they have no constructor arguments. if (token instanceof Function) { return getUndecoratedInjectableFactory(token); } // There was no way to resolve a factory for this token. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'unreachable'); } function getUndecoratedInjectableFactory(token) { // If the token has parameters then it has dependencies that we cannot resolve implicitly. const paramLength = token.length; if (paramLength > 0) { throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Can't resolve all parameters for ${stringify(token)}: (${newArray(paramLength, '?').join(', ')}).`); } // The constructor function appears to have no parameters. // This might be because it inherits from a super-class. In which case, use an injectable // def from an ancestor if there is one. // Otherwise this really is a simple class with no dependencies, so return a factory that // just instantiates the zero-arg constructor. const inheritedInjectableDef = getInheritedInjectableDef(token); if (inheritedInjectableDef !== null) { return () => inheritedInjectableDef.factory(token); } else { return () => new token(); } } function providerToRecord(provider) { if (isValueProvider(provider)) { return makeRecord(undefined, provider.useValue); } else { const factory = providerToFactory(provider); return makeRecord(factory, NOT_YET); } } /** * Converts a `SingleProvider` into a factory function. * * @param provider provider to convert to factory */ function providerToFactory(provider, ngModuleType, providers) { let factory = undefined; if (ngDevMode && isEnvironmentProviders(provider)) { throwInvalidProviderError(undefined, providers, provider); } if (isTypeProvider(provider)) { const unwrappedProvider = resolveForwardRef(provider); return getFactoryDef(unwrappedProvider) || injectableDefOrInjectorDefFactory(unwrappedProvider); } else { if (isValueProvider(provider)) { factory = () => resolveForwardRef(provider.useValue); } else if (isFactoryProvider(provider)) { factory = () => provider.useFactory(...injectArgs(provider.deps || [])); } else if (isExistingProvider(provider)) { factory = (_, flags) => ɵɵinject(resolveForwardRef(provider.useExisting), flags !== undefined && flags & InjectFlags.Optional ? InjectFlags.Optional : undefined); } else { const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide)); if (ngDevMode && !classRef) { throwInvalidProviderError(ngModuleType, providers, provider); } if (hasDeps(provider)) { factory = () => new classRef(...injectArgs(provider.deps)); } else { return getFactoryDef(classRef) || injectableDefOrInjectorDefFactory(classRef); } } } return factory; } function assertNotDestroyed(injector) { if (injector.destroyed) { throw new RuntimeError(205 /* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED */, ngDevMode && 'Injector has already been destroyed.'); } } function makeRecord(factory, value, multi = false) { return { factory: factory, value: value, multi: multi ? [] : undefined, }; } function hasDeps(value) { return !!value.deps; } function hasOnDestroy(value) { return (value !== null && typeof value === 'object' && typeof value.ngOnDestroy === 'function'); } function couldBeInjectableType(value) { return (typeof value === 'function' || (typeof value === 'object' && value instanceof InjectionToken)); } function forEachSingleProvider(providers, fn) { for (const provider of providers) { if (Array.isArray(provider)) { forEachSingleProvider(provider, fn); } else if (provider && isEnvironmentProviders(provider)) { forEachSingleProvider(provider.ɵproviders, fn); } else { fn(provider); } } } /** * Runs the given function in the [context](guide/di/dependency-injection-context) of the given * `Injector`. * * Within the function's stack frame, [`inject`](api/core/inject) can be used to inject dependencies * from the given `Injector`. Note that `inject` is only usable synchronously, and cannot be used in * any asynchronous callbacks or after any `await` points. * * @param injector the injector which will satisfy calls to [`inject`](api/core/inject) while `fn` * is executing * @param fn the closure to be run in the context of `injector` * @returns the return value of the function, if any * @publicApi */ function runInInjectionContext(injector, fn) { let internalInjector; if (injector instanceof R3Injector) { assertNotDestroyed(injector); internalInjector = injector; } else { internalInjector = new RetrievingInjector(injector); } let prevInjectorProfilerContext; if (ngDevMode) { prevInjectorProfilerContext = setInjectorProfilerContext({ injector, token: null }); } const prevInjector = setCurrentInjector(internalInjector); const previousInjectImplementation = setInjectImplementation(undefined); try { return fn(); } finally { setCurrentInjector(prevInjector); ngDevMode && setInjectorProfilerContext(prevInjectorProfilerContext); setInjectImplementation(previousInjectImplementation); } } /** * Whether the current stack frame is inside an injection context. */ function isInInjectionContext() { return getInjectImplementation() !== undefined || getCurrentInjector() != null; } /** * Asserts that the current stack frame is within an [injection * context](guide/di/dependency-injection-context) and has access to `inject`. * * @param debugFn a reference to the function making the assertion (used for the error message). * * @publicApi */ function assertInInjectionContext(debugFn) { // Taking a `Function` instead of a string name here prevents the unminified name of the function // from being retained in the bundle regardless of minification. if (!isInInjectionContext()) { throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode && debugFn.name + '() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`'); } } var FactoryTarget; (function (FactoryTarget) { FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive"; FactoryTarget[FactoryTarget["Component"] = 1] = "Component"; FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable"; FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe"; FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule"; })(FactoryTarget || (FactoryTarget = {})); var R3TemplateDependencyKind; (function (R3TemplateDependencyKind) { R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive"; R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe"; R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule"; })(R3TemplateDependencyKind || (R3TemplateDependencyKind = {})); var ViewEncapsulation$1; (function (ViewEncapsulation) { ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated"; // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None"; ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom"; })(ViewEncapsulation$1 || (ViewEncapsulation$1 = {})); function getCompilerFacade(request) { const globalNg = _global['ng']; if (globalNg && globalNg.ɵcompilerFacade) { return globalNg.ɵcompilerFacade; } if (typeof ngDevMode === 'undefined' || ngDevMode) { // Log the type as an error so that a developer can easily navigate to the type from the // console. console.error(`JIT compilation failed for ${request.kind}`, request.type); let message = `The ${request.kind} '${request.type.name}' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.\n\n`; if (request.usage === 1 /* JitCompilerUsage.PartialDeclaration */) { message += `The ${request.kind} is part of a library that has been partially compiled.\n`; message += `However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.\n`; message += '\n'; message += `Ideally, the library is processed using the Angular Linker to become fully AOT compiled.\n`; } else { message += `JIT compilation is discouraged for production use-cases! Consider using AOT mode instead.\n`; } message += `Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',\n`; message += `or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.`; throw new Error(message); } else { throw new Error('JIT compiler unavailable'); } } /** * A mapping of the @angular/core API surface used in generated expressions to the actual symbols. * * This should be kept up to date with the public exports of @angular/core. */ const angularCoreDiEnv = { 'ɵɵdefineInjectable': ɵɵdefineInjectable, 'ɵɵdefineInjector': ɵɵdefineInjector, 'ɵɵinject': ɵɵinject, 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep, 'resolveForwardRef': resolveForwardRef, }; /** * @description * * Represents a type that a Component or other object is instances of. * * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by * the `MyCustomComponent` constructor function. * * @publicApi */ const Type = Function; function isType(v) { return typeof v === 'function'; } /* * ######################### * Attention: These Regular expressions have to hold even if the code is minified! * ########################## */ /** * Regular expression that detects pass-through constructors for ES5 output. This Regex * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also * it intends to capture the pattern where existing constructors have been downleveled from * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g. * * ```ts * function MyClass() { * var _this = _super.apply(this, arguments) || this; * ``` * * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2: * ```ts * function MyClass() { * var _this = _super.apply(this, __spread(arguments)) || this; * ``` * * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2: * ```ts * function MyClass() { * var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this; * ``` * * More details can be found in: https://github.com/angular/angular/issues/38453. */ const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\).*)\)/; /** Regular expression that detects ES2015 classes which extend from other classes. */ const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; /** * Regular expression that detects ES2015 classes which extend from other classes and * have an explicit constructor defined. */ const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/; /** * Regular expression that detects ES2015 classes which extend from other classes * and inherit a constructor. */ const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/; /** * Determine whether a stringified type is a class which delegates its constructor * to its parent. * * This is not trivial since compiled code can actually contain a constructor function * even if the original source code did not. For instance, when the child class contains * an initialized instance property. */ function isDelegateCtor(typeStr) { return (ES5_DELEGATE_CTOR.test(typeStr) || ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) || (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr))); } class ReflectionCapabilities { _reflect; constructor(reflect) { this._reflect = reflect || _global['Reflect']; } factory(t) { return (...args) => new t(...args); } /** @internal */ _zipTypesAndAnnotations(paramTypes, paramAnnotations) { let result; if (typeof paramTypes === 'undefined') { result = newArray(paramAnnotations.length); } else { result = newArray(paramTypes.length); } for (let i = 0; i < result.length; i++) { // TS outputs Object for parameters without types, while Traceur omits // the annotations. For now we preserve the Traceur behavior to aid // migration, but this can be revisited. if (typeof paramTypes === 'undefined') { result[i] = []; } else if (paramTypes[i] && paramTypes[i] != Object) { result[i] = [paramTypes[i]]; } else { result[i] = []; } if (paramAnnotations && paramAnnotations[i] != null) { result[i] = result[i].concat(paramAnnotations[i]); } } return result; } _ownParameters(type, parentCtor) { const typeStr = type.toString(); // If we have no decorators, we only have function.length as metadata. // In that case, to detect whether a child class declared an own constructor or not, // we need to look inside of that constructor to check whether it is // just calling the parent. // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439 // that sets 'design:paramtypes' to [] // if a class inherits from another class but has no ctor declared itself. if (isDelegateCtor(typeStr)) { return null; } // Prefer the direct API. if (type.parameters && type.parameters !== parentCtor.parameters) { return type.parameters; } // API of tsickle for lowering decorators to properties on the class. const tsickleCtorParams = type.ctorParameters; if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) { // Newer tsickle uses a function closure // Retain the non-function case for compatibility with older tsickle const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams; const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type); const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators)); return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // API for metadata created by invoking the decorators. const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS]; const paramTypes = this._reflect && this._reflect.getOwnMetadata && this._reflect.getOwnMetadata('design:paramtypes', type); if (paramTypes || paramAnnotations) { return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // If a class has no decorators, at least create metadata // based on function.length. // Note: We know that this is a real constructor as we checked // the content of the constructor above. return newArray(type.length); } parameters(type) { // Note: only report metadata if we have at least one class decorator // to stay in sync with the static reflector. if (!isType(type)) { return []; } const parentCtor = getParentCtor(type); let parameters = this._ownParameters(type, parentCtor); if (!parameters && parentCtor !== Object) { parameters = this.parameters(parentCtor); } return parameters || []; } _ownAnnotations(typeOrFunc, parentCtor) { // Prefer the direct API. if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) { let annotations = typeOrFunc.annotations; if (typeof annotations === 'function' && annotations.annotations) { annotations = annotations.annotations; } return annotations; } // API of tsickle for lowering decorators to properties on the class. if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) { return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators); } // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) { return typeOrFunc[ANNOTATIONS]; } return null; } annotations(typeOrFunc) { if (!isType(typeOrFunc)) { return []; } const parentCtor = getParentCtor(typeOrFunc); const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || []; const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : []; return parentAnnotations.concat(ownAnnotations); } _ownPropMetadata(typeOrFunc, parentCtor) { // Prefer the direct API. if (typeOrFunc.propMetadata && typeOrFunc.propMetadata !== parentCtor.propMetadata) { let propMetadata = typeOrFunc.propMetadata; if (typeof propMetadata === 'function' && propMetadata.propMetadata) { propMetadata = propMetadata.propMetadata; } return propMetadata; } // API of tsickle for lowering decorators to properties on the class. if (typeOrFunc.propDecorators && typeOrFunc.propDecorators !== parentCtor.propDecorators) { const propDecorators = typeOrFunc.propDecorators; const propMetadata = {}; Object.keys(propDecorators).forEach((prop) => { propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]); }); return propMetadata; } // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(PROP_METADATA)) { return typeOrFunc[PROP_METADATA]; } return null; } propMetadata(typeOrFunc) { if (!isType(typeOrFunc)) { return {}; } const parentCtor = getParentCtor(typeOrFunc); const propMetadata = {}; if (parentCtor !== Object) { const parentPropMetadata = this.propMetadata(parentCtor); Object.keys(parentPropMetadata).forEach((propName) => { propMetadata[propName] = parentPropMetadata[propName]; }); } const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor); if (ownPropMetadata) { Object.keys(ownPropMetadata).forEach((propName) => { const decorators = []; if (propMetadata.hasOwnProperty(propName)) { decorators.push(...propMetadata[propName]); } decorators.push(...ownPropMetadata[propName]); propMetadata[propName] = decorators; }); } return propMetadata; } ownPropMetadata(typeOrFunc) { if (!isType(typeOrFunc)) { return {}; } return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {}; } hasLifecycleHook(type, lcProperty) { return type instanceof Type && lcProperty in type.prototype; } } function convertTsickleDecoratorIntoMetadata(decoratorInvocations) { if (!decoratorInvocations) { return []; } return decoratorInvocations.map((decoratorInvocation) => { const decoratorType = decoratorInvocation.type; const annotationCls = decoratorType.annotationCls; const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : []; return new annotationCls(...annotationArgs); }); } function getParentCtor(ctor) { const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null; const parentCtor = parentProto ? parentProto.constructor : null; // Note: We always use `Object` as the null value // to simplify checking later on. return parentCtor || Object; } // Below are constants for LView indices to help us look up LView members // without having to remember the specific indices. // Uglify will inline these when minifying so there shouldn't be a cost. const HOST = 0; const TVIEW = 1; // Shared with LContainer const FLAGS = 2; const PARENT = 3; const NEXT = 4; const T_HOST = 5; // End shared with LContainer const HYDRATION = 6; const CLEANUP = 7; const CONTEXT = 8; const INJECTOR = 9; const ENVIRONMENT = 10; const RENDERER = 11; const CHILD_HEAD = 12; const CHILD_TAIL = 13; // FIXME(misko): Investigate if the three declarations aren't all same thing. const DECLARATION_VIEW = 14; const DECLARATION_COMPONENT_VIEW = 15; const DECLARATION_LCONTAINER = 16; const PREORDER_HOOK_FLAGS = 17; const QUERIES = 18; const ID = 19; const EMBEDDED_VIEW_INJECTOR = 20; const ON_DESTROY_HOOKS = 21; const EFFECTS_TO_SCHEDULE = 22; const EFFECTS = 23; const REACTIVE_TEMPLATE_CONSUMER = 24; const AFTER_RENDER_SEQUENCES_TO_ADD = 25; /** * Size of LView's header. Necessary to adjust for it when setting slots. * * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate * instruction index into `LView` index. All other indexes should be in the `LView` index space and * there should be no need to refer to `HEADER_OFFSET` anywhere else. */ const HEADER_OFFSET = 26; /** * Special location which allows easy identification of type. If we have an array which was * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is * `LContainer`. */ const TYPE = 1; /** * Below are constants for LContainer indices to help us look up LContainer members * without having to remember the specific indices. * Uglify will inline these when minifying so there shouldn't be a cost. */ // FLAGS, PARENT, NEXT, and T_HOST are indices 2, 3, 4, and 5 // As we already have these constants in LView, we don't need to re-create them. const DEHYDRATED_VIEWS = 6; const NATIVE = 7; const VIEW_REFS = 8; const MOVED_VIEWS = 9; /** * Size of LContainer's header. Represents the index after which all views in the * container will be inserted. We need to keep a record of current views so we know * which views are already in the DOM (and don't need to be re-added) and so we can * remove views from the DOM when they are no longer required. */ const CONTAINER_HEADER_OFFSET = 10; /** * True if `value` is `LView`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function isLView(value) { return Array.isArray(value) && typeof value[TYPE] === 'object'; } /** * True if `value` is `LContainer`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function isLContainer(value) { return Array.isArray(value) && value[TYPE] === true; } function isContentQueryHost(tNode) { return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0; } function isComponentHost(tNode) { return tNode.componentOffset > -1; } function isDirectiveHost(tNode) { return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */; } function isComponentDef(def) { return !!def.template; } function isRootView(target) { // Determines whether a given LView is marked as a root view. return (target[FLAGS] & 512 /* LViewFlags.IsRoot */) !== 0; } function isProjectionTNode(tNode) { return (tNode.type & 16 /* TNodeType.Projection */) === 16 /* TNodeType.Projection */; } function hasI18n(lView) { return (lView[FLAGS] & 32 /* LViewFlags.HasI18n */) === 32 /* LViewFlags.HasI18n */; } function isDestroyed(lView) { // Determines whether a given LView is marked as destroyed. return (lView[FLAGS] & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */; } // [Assert functions do not constraint type when they are guarded by a truthy // expression.](https://github.com/microsoft/TypeScript/issues/37295) function assertTNodeForLView(tNode, lView) { assertTNodeForTView(tNode, lView[TVIEW]); } function assertTNodeForTView(tNode, tView) { assertTNode(tNode); const tData = tView.data; for (let i = HEADER_OFFSET; i < tData.length; i++) { if (tData[i] === tNode) { return; } } throwError('This TNode does not belong to this TView.'); } function assertTNode(tNode) { assertDefined(tNode, 'TNode must be defined'); if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) { throwError('Not of type TNode, got: ' + tNode); } } function assertTIcu(tIcu) { assertDefined(tIcu, 'Expected TIcu to be defined'); if (!(typeof tIcu.currentCaseLViewIndex === 'number')) { throwError('Object is not of TIcu type.'); } } function assertComponentType(actual, msg = "Type passed in is not ComponentType, it does not have 'ɵcmp' property.") { if (!getComponentDef(actual)) { throwError(msg); } } function assertNgModuleType(actual, msg = "Type passed in is not NgModuleType, it does not have 'ɵmod' property.") { if (!getNgModuleDef(actual)) { throwError(msg); } } function assertHasParent(tNode) { assertDefined(tNode, 'currentTNode should exist!'); assertDefined(tNode.parent, 'currentTNode should have a parent'); } function assertLContainer(value) { assertDefined(value, 'LContainer must be defined'); assertEqual(isLContainer(value), true, 'Expecting LContainer'); } function assertLViewOrUndefined(value) { value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null'); } function assertLView(value) { assertDefined(value, 'LView must be defined'); assertEqual(isLView(value), true, 'Expecting LView'); } function assertFirstCreatePass(tView, errMessage) { assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.'); } function assertFirstUpdatePass(tView, errMessage) { assertEqual(tView.firstUpdatePass, true, 'Should only be called in first update pass.'); } /** * This is a basic sanity check that an object is probably a directive def. DirectiveDef is * an interface, so we can't do a direct instanceof check. */ function assertDirectiveDef(obj) { if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) { throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`); } } function assertIndexInDeclRange(tView, index) { assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index); } function assertIndexInExpandoRange(lView, index) { const tView = lView[1]; assertBetween(tView.expandoStartIndex, lView.length, index); } function assertBetween(lower, upper, index) { if (!(lower <= index && index < upper)) { throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`); } } function assertProjectionSlots(lView, errMessage) { assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.'); assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, 'Components with projection nodes () must have projection slots defined.'); } function assertParentView(lView, errMessage) { assertDefined(lView, "Component views should always have a parent view (component's host view)"); } /** * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a * NodeInjector data structure. * * @param lView `LView` which should be checked. * @param injectorIndex index into the `LView` where the `NodeInjector` is expected. */ function assertNodeInjector(lView, injectorIndex) { assertIndexInExpandoRange(lView, injectorIndex); assertIndexInExpandoRange(lView, injectorIndex + 8 /* NodeInjectorOffset.PARENT */); assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */], 'injectorIndex should point to parent injector'); } /** * Represents a basic change from a previous to a new value for a single * property on a directive instance. Passed as a value in a * {@link SimpleChanges} object to the `ngOnChanges` hook. * * @see {@link OnChanges} * * @publicApi */ class SimpleChange { previousValue; currentValue; firstChange; constructor(previousValue, currentValue, firstChange) { this.previousValue = previousValue; this.currentValue = currentValue; this.firstChange = firstChange; } /** * Check whether the new value is the first value assigned. */ isFirstChange() { return this.firstChange; } } function applyValueToInputField(instance, inputSignalNode, privateName, value) { if (inputSignalNode !== null) { inputSignalNode.applyValueToInputSignal(inputSignalNode, value); } else { instance[privateName] = value; } } /** * The NgOnChangesFeature decorates a component with support for the ngOnChanges * lifecycle hook, so it should be included in any component that implements * that hook. * * If the component or directive uses inheritance, the NgOnChangesFeature MUST * be included as a feature AFTER {@link InheritDefinitionFeature}, otherwise * inherited properties will not be propagated to the ngOnChanges lifecycle * hook. * * Example usage: * * ```ts * static ɵcmp = defineComponent({ * ... * inputs: {name: 'publicName'}, * features: [NgOnChangesFeature] * }); * ``` * * @codeGenApi */ const ɵɵNgOnChangesFeature = /* @__PURE__ */ (() => { const ɵɵNgOnChangesFeatureImpl = () => NgOnChangesFeatureImpl; // This option ensures that the ngOnChanges lifecycle hook will be inherited // from superclasses (in InheritDefinitionFeature). /** @nocollapse */ ɵɵNgOnChangesFeatureImpl.ngInherit = true; return ɵɵNgOnChangesFeatureImpl; })(); function NgOnChangesFeatureImpl(definition) { if (definition.type.prototype.ngOnChanges) { definition.setInput = ngOnChangesSetInput; } return rememberChangeHistoryAndInvokeOnChangesHook; } /** * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate * `ngOnChanges`. * * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are * found it invokes `ngOnChanges` on the component instance. * * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`, * it is guaranteed to be called with component instance. */ function rememberChangeHistoryAndInvokeOnChangesHook() { const simpleChangesStore = getSimpleChangesStore(this); const current = simpleChangesStore?.current; if (current) { const previous = simpleChangesStore.previous; if (previous === EMPTY_OBJ) { simpleChangesStore.previous = current; } else { // New changes are copied to the previous store, so that we don't lose history for inputs // which were not changed this time for (let key in current) { previous[key] = current[key]; } } simpleChangesStore.current = null; this.ngOnChanges(current); } } function ngOnChangesSetInput(instance, inputSignalNode, value, publicName, privateName) { const declaredName = this.declaredInputs[publicName]; ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string'); const simpleChangesStore = getSimpleChangesStore(instance) || setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null }); const current = simpleChangesStore.current || (simpleChangesStore.current = {}); const previous = simpleChangesStore.previous; const previousChange = previous[declaredName]; current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ); applyValueToInputField(instance, inputSignalNode, privateName, value); } const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__'; function getSimpleChangesStore(instance) { return instance[SIMPLE_CHANGES_STORE] || null; } function setSimpleChangesStore(instance, store) { return (instance[SIMPLE_CHANGES_STORE] = store); } let profilerCallback = null; /** * Sets the callback function which will be invoked before and after performing certain actions at * runtime (for example, before and after running change detection). * * Warning: this function is *INTERNAL* and should not be relied upon in application's code. * The contract of the function might be changed in any release and/or the function can be removed * completely. * * @param profiler function provided by the caller or null value to disable profiling. */ const setProfiler = (profiler) => { profilerCallback = profiler; }; /** * Profiler function which wraps user code executed by the runtime. * * @param event ProfilerEvent corresponding to the execution context * @param instance component instance * @param hookOrListener lifecycle hook function or output listener. The value depends on the * execution context * @returns */ const profiler = function (event, instance = null, hookOrListener) { if (profilerCallback != null /* both `null` and `undefined` */) { profilerCallback(event, instance, hookOrListener); } }; const SVG_NAMESPACE = 'svg'; const MATH_ML_NAMESPACE = 'math'; /** * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`) * in same location in `LView`. This is because we don't want to pre-allocate space for it * because the storage is sparse. This file contains utilities for dealing with such data types. * * How do we know what is stored at a given location in `LView`. * - `Array.isArray(value) === false` => `RNode` (The normal storage value) * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value. * - `typeof value[TYPE] === 'object'` => `LView` * - This happens when we have a component at a given location * - `typeof value[TYPE] === true` => `LContainer` * - This happens when we have `LContainer` binding at a given location. * * * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient. */ /** * Returns `RNode`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function unwrapRNode(value) { while (Array.isArray(value)) { value = value[HOST]; } return value; } /** * Returns `LView` or `null` if not found. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function unwrapLView(value) { while (Array.isArray(value)) { // This check is same as `isLView()` but we don't call at as we don't want to call // `Array.isArray()` twice and give JITer more work for inlining. if (typeof value[TYPE] === 'object') return value; value = value[HOST]; } return null; } /** * Retrieves an element value from the provided `viewData`, by unwrapping * from any containers, component views, or style contexts. */ function getNativeByIndex(index, lView) { ngDevMode && assertIndexInRange(lView, index); ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET'); return unwrapRNode(lView[index]); } /** * Retrieve an `RNode` for a given `TNode` and `LView`. * * This function guarantees in dev mode to retrieve a non-null `RNode`. * * @param tNode * @param lView */ function getNativeByTNode(tNode, lView) { ngDevMode && assertTNodeForLView(tNode, lView); ngDevMode && assertIndexInRange(lView, tNode.index); const node = unwrapRNode(lView[tNode.index]); return node; } /** * Retrieve an `RNode` or `null` for a given `TNode` and `LView`. * * Some `TNode`s don't have associated `RNode`s. For example `Projection` * * @param tNode * @param lView */ function getNativeByTNodeOrNull(tNode, lView) { const index = tNode === null ? -1 : tNode.index; if (index !== -1) { ngDevMode && assertTNodeForLView(tNode, lView); const node = unwrapRNode(lView[index]); return node; } return null; } // fixme(misko): The return Type should be `TNode|null` function getTNode(tView, index) { ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode'); ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode'); const tNode = tView.data[index]; ngDevMode && tNode !== null && assertTNode(tNode); return tNode; } /** Retrieves a value from any `LView` or `TData`. */ function load(view, index) { ngDevMode && assertIndexInRange(view, index); return view[index]; } /** Store a value in the `data` at a given `index`. */ function store(tView, lView, index, value) { // We don't store any static data for local variables, so the first time // we see the template, we should store as null to avoid a sparse array if (index >= tView.data.length) { tView.data[index] = null; tView.blueprint[index] = null; } lView[index] = value; } function getComponentLViewByIndex(nodeIndex, hostView) { // Could be an LView or an LContainer. If LContainer, unwrap to find LView. ngDevMode && assertIndexInRange(hostView, nodeIndex); const slotValue = hostView[nodeIndex]; const lView = isLView(slotValue) ? slotValue : slotValue[HOST]; return lView; } /** Checks whether a given view is in creation mode */ function isCreationMode(view) { return (view[FLAGS] & 4 /* LViewFlags.CreationMode */) === 4 /* LViewFlags.CreationMode */; } /** * Returns a boolean for whether the view is attached to the change detection tree. * * Note: This determines whether a view should be checked, not whether it's inserted * into a container. For that, you'll want `viewAttachedToContainer` below. */ function viewAttachedToChangeDetector(view) { return (view[FLAGS] & 128 /* LViewFlags.Attached */) === 128 /* LViewFlags.Attached */; } /** Returns a boolean for whether the view is attached to a container. */ function viewAttachedToContainer(view) { return isLContainer(view[PARENT]); } function getConstant(consts, index) { if (index === null || index === undefined) return null; ngDevMode && assertIndexInRange(consts, index); return consts[index]; } /** * Resets the pre-order hook flags of the view. * @param lView the LView on which the flags are reset */ function resetPreOrderHookFlags(lView) { lView[PREORDER_HOOK_FLAGS] = 0; } /** * Adds the `RefreshView` flag from the lView and updates HAS_CHILD_VIEWS_TO_REFRESH flag of * parents. */ function markViewForRefresh(lView) { if (lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) { return; } lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */; if (viewAttachedToChangeDetector(lView)) { markAncestorsForTraversal(lView); } } /** * Walks up the LView hierarchy. * @param nestingLevel Number of times to walk up in hierarchy. * @param currentView View from which to start the lookup. */ function walkUpViews(nestingLevel, currentView) { while (nestingLevel > 0) { ngDevMode && assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.'); currentView = currentView[DECLARATION_VIEW]; nestingLevel--; } return currentView; } function requiresRefreshOrTraversal(lView) { return !!(lView[FLAGS] & (1024 /* LViewFlags.RefreshView */ | 8192 /* LViewFlags.HasChildViewsToRefresh */) || lView[REACTIVE_TEMPLATE_CONSUMER]?.dirty); } /** * Updates the `HasChildViewsToRefresh` flag on the parents of the `LView` as well as the * parents above. */ function updateAncestorTraversalFlagsOnAttach(lView) { lView[ENVIRONMENT].changeDetectionScheduler?.notify(8 /* NotificationSource.ViewAttached */); if (lView[FLAGS] & 64 /* LViewFlags.Dirty */) { lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */; } if (requiresRefreshOrTraversal(lView)) { markAncestorsForTraversal(lView); } } /** * Ensures views above the given `lView` are traversed during change detection even when they are * not dirty. * * This is done by setting the `HAS_CHILD_VIEWS_TO_REFRESH` flag up to the root, stopping when the * flag is already `true` or the `lView` is detached. */ function markAncestorsForTraversal(lView) { lView[ENVIRONMENT].changeDetectionScheduler?.notify(0 /* NotificationSource.MarkAncestorsForTraversal */); let parent = getLViewParent(lView); while (parent !== null) { // We stop adding markers to the ancestors once we reach one that already has the marker. This // is to avoid needlessly traversing all the way to the root when the marker already exists. if (parent[FLAGS] & 8192 /* LViewFlags.HasChildViewsToRefresh */) { break; } parent[FLAGS] |= 8192 /* LViewFlags.HasChildViewsToRefresh */; if (!viewAttachedToChangeDetector(parent)) { break; } parent = getLViewParent(parent); } } /** * Stores a LView-specific destroy callback. */ function storeLViewOnDestroy(lView, onDestroyCallback) { if (isDestroyed(lView)) { throw new RuntimeError(911 /* RuntimeErrorCode.VIEW_ALREADY_DESTROYED */, ngDevMode && 'View has already been destroyed.'); } if (lView[ON_DESTROY_HOOKS] === null) { lView[ON_DESTROY_HOOKS] = []; } lView[ON_DESTROY_HOOKS].push(onDestroyCallback); } /** * Removes previously registered LView-specific destroy callback. */ function removeLViewOnDestroy(lView, onDestroyCallback) { if (lView[ON_DESTROY_HOOKS] === null) return; const destroyCBIdx = lView[ON_DESTROY_HOOKS].indexOf(onDestroyCallback); if (destroyCBIdx !== -1) { lView[ON_DESTROY_HOOKS].splice(destroyCBIdx, 1); } } /** * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of * that LContainer, which is an LView * @param lView the lView whose parent to get */ function getLViewParent(lView) { ngDevMode && assertLView(lView); const parent = lView[PARENT]; return isLContainer(parent) ? parent[PARENT] : parent; } function getOrCreateLViewCleanup(view) { // top level variables should not be exported for performance reasons (PERF_NOTES.md) return (view[CLEANUP] ??= []); } function getOrCreateTViewCleanup(tView) { return (tView.cleanup ??= []); } /** * Saves context for this cleanup function in LView.cleanupInstances. * * On the first template pass, saves in TView: * - Cleanup function * - Index of context we just saved in LView.cleanupInstances */ function storeCleanupWithContext(tView, lView, context, cleanupFn) { const lCleanup = getOrCreateLViewCleanup(lView); // Historically the `storeCleanupWithContext` was used to register both framework-level and // user-defined cleanup callbacks, but over time those two types of cleanups were separated. // This dev mode checks assures that user-level cleanup callbacks are _not_ stored in data // structures reserved for framework-specific hooks. ngDevMode && assertDefined(context, 'Cleanup context is mandatory when registering framework-level destroy hooks'); lCleanup.push(context); if (tView.firstCreatePass) { getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1); } else { // Make sure that no new framework-level cleanup functions are registered after the first // template pass is done (and TView data structures are meant to fully constructed). if (ngDevMode) { Object.freeze(getOrCreateTViewCleanup(tView)); } } } const instructionState = { lFrame: createLFrame(null), bindingsEnabled: true, skipHydrationRootTNode: null, }; var CheckNoChangesMode; (function (CheckNoChangesMode) { CheckNoChangesMode[CheckNoChangesMode["Off"] = 0] = "Off"; CheckNoChangesMode[CheckNoChangesMode["Exhaustive"] = 1] = "Exhaustive"; CheckNoChangesMode[CheckNoChangesMode["OnlyDirtyViews"] = 2] = "OnlyDirtyViews"; })(CheckNoChangesMode || (CheckNoChangesMode = {})); /** * In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error. * * Necessary to support ChangeDetectorRef.checkNoChanges(). * * The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended * changes exist in the change detector or its children. */ let _checkNoChangesMode = 0; /* CheckNoChangesMode.Off */ /** * Flag used to indicate that we are in the middle running change detection on a view * * @see detectChangesInViewWhileDirty */ let _isRefreshingViews = false; function getElementDepthCount() { return instructionState.lFrame.elementDepthCount; } function increaseElementDepthCount() { instructionState.lFrame.elementDepthCount++; } function decreaseElementDepthCount() { instructionState.lFrame.elementDepthCount--; } function getBindingsEnabled() { return instructionState.bindingsEnabled; } /** * Returns true if currently inside a skip hydration block. * @returns boolean */ function isInSkipHydrationBlock$1() { return instructionState.skipHydrationRootTNode !== null; } /** * Returns true if this is the root TNode of the skip hydration block. * @param tNode the current TNode * @returns boolean */ function isSkipHydrationRootTNode(tNode) { return instructionState.skipHydrationRootTNode === tNode; } /** * Enables directive matching on elements. * * * Example: * ```html * * Should match component / directive. * *
* * * Should not match component / directive because we are in ngNonBindable. * * *
* ``` * * @codeGenApi */ function ɵɵenableBindings() { instructionState.bindingsEnabled = true; } /** * Sets a flag to specify that the TNode is in a skip hydration block. * @param tNode the current TNode */ function enterSkipHydrationBlock(tNode) { instructionState.skipHydrationRootTNode = tNode; } /** * Disables directive matching on element. * * * Example: * ```html * * Should match component / directive. * *
* * * Should not match component / directive because we are in ngNonBindable. * * *
* ``` * * @codeGenApi */ function ɵɵdisableBindings() { instructionState.bindingsEnabled = false; } /** * Clears the root skip hydration node when leaving a skip hydration block. */ function leaveSkipHydrationBlock() { instructionState.skipHydrationRootTNode = null; } /** * Return the current `LView`. */ function getLView() { return instructionState.lFrame.lView; } /** * Return the current `TView`. */ function getTView() { return instructionState.lFrame.tView; } /** * Restores `contextViewData` to the given OpaqueViewState instance. * * Used in conjunction with the getCurrentView() instruction to save a snapshot * of the current view and restore it when listeners are invoked. This allows * walking the declaration view tree in listeners to get vars from parent views. * * @param viewToRestore The OpaqueViewState instance to restore. * @returns Context of the restored OpaqueViewState instance. * * @codeGenApi */ function ɵɵrestoreView(viewToRestore) { instructionState.lFrame.contextLView = viewToRestore; return viewToRestore[CONTEXT]; } /** * Clears the view set in `ɵɵrestoreView` from memory. Returns the passed in * value so that it can be used as a return value of an instruction. * * @codeGenApi */ function ɵɵresetView(value) { instructionState.lFrame.contextLView = null; return value; } function getCurrentTNode() { let currentTNode = getCurrentTNodePlaceholderOk(); while (currentTNode !== null && currentTNode.type === 64 /* TNodeType.Placeholder */) { currentTNode = currentTNode.parent; } return currentTNode; } function getCurrentTNodePlaceholderOk() { return instructionState.lFrame.currentTNode; } function getCurrentParentTNode() { const lFrame = instructionState.lFrame; const currentTNode = lFrame.currentTNode; return lFrame.isParent ? currentTNode : currentTNode.parent; } function setCurrentTNode(tNode, isParent) { ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView); const lFrame = instructionState.lFrame; lFrame.currentTNode = tNode; lFrame.isParent = isParent; } function isCurrentTNodeParent() { return instructionState.lFrame.isParent; } function setCurrentTNodeAsNotParent() { instructionState.lFrame.isParent = false; } function getContextLView() { const contextLView = instructionState.lFrame.contextLView; ngDevMode && assertDefined(contextLView, 'contextLView must be defined.'); return contextLView; } function isInCheckNoChangesMode() { !ngDevMode && throwError('Must never be called in production mode'); return _checkNoChangesMode !== CheckNoChangesMode.Off; } function isExhaustiveCheckNoChanges() { !ngDevMode && throwError('Must never be called in production mode'); return _checkNoChangesMode === CheckNoChangesMode.Exhaustive; } function setIsInCheckNoChangesMode(mode) { !ngDevMode && throwError('Must never be called in production mode'); _checkNoChangesMode = mode; } function isRefreshingViews() { return _isRefreshingViews; } function setIsRefreshingViews(mode) { const prev = _isRefreshingViews; _isRefreshingViews = mode; return prev; } // top level variables should not be exported for performance reasons (PERF_NOTES.md) function getBindingRoot() { const lFrame = instructionState.lFrame; let index = lFrame.bindingRootIndex; if (index === -1) { index = lFrame.bindingRootIndex = lFrame.tView.bindingStartIndex; } return index; } function getBindingIndex() { return instructionState.lFrame.bindingIndex; } function setBindingIndex(value) { return (instructionState.lFrame.bindingIndex = value); } function nextBindingIndex() { return instructionState.lFrame.bindingIndex++; } function incrementBindingIndex(count) { const lFrame = instructionState.lFrame; const index = lFrame.bindingIndex; lFrame.bindingIndex = lFrame.bindingIndex + count; return index; } function isInI18nBlock() { return instructionState.lFrame.inI18n; } function setInI18nBlock(isInI18nBlock) { instructionState.lFrame.inI18n = isInI18nBlock; } /** * Set a new binding root index so that host template functions can execute. * * Bindings inside the host template are 0 index. But because we don't know ahead of time * how many host bindings we have we can't pre-compute them. For this reason they are all * 0 index and we just shift the root so that they match next available location in the LView. * * @param bindingRootIndex Root index for `hostBindings` * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive * whose `hostBindings` are being processed. */ function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) { const lFrame = instructionState.lFrame; lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex; setCurrentDirectiveIndex(currentDirectiveIndex); } /** * When host binding is executing this points to the directive index. * `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef` * `LView[getCurrentDirectiveIndex()]` is directive instance. */ function getCurrentDirectiveIndex() { return instructionState.lFrame.currentDirectiveIndex; } /** * Sets an index of a directive whose `hostBindings` are being processed. * * @param currentDirectiveIndex `TData` index where current directive instance can be found. */ function setCurrentDirectiveIndex(currentDirectiveIndex) { instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex; } /** * Retrieve the current `DirectiveDef` which is active when `hostBindings` instruction is being * executed. * * @param tData Current `TData` where the `DirectiveDef` will be looked up at. */ function getCurrentDirectiveDef(tData) { const currentDirectiveIndex = instructionState.lFrame.currentDirectiveIndex; return currentDirectiveIndex === -1 ? null : tData[currentDirectiveIndex]; } function getCurrentQueryIndex() { return instructionState.lFrame.currentQueryIndex; } function setCurrentQueryIndex(value) { instructionState.lFrame.currentQueryIndex = value; } /** * Returns a `TNode` of the location where the current `LView` is declared at. * * @param lView an `LView` that we want to find parent `TNode` for. */ function getDeclarationTNode(lView) { const tView = lView[TVIEW]; // Return the declaration parent for embedded views if (tView.type === 2 /* TViewType.Embedded */) { ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.'); return tView.declTNode; } // Components don't have `TView.declTNode` because each instance of component could be // inserted in different location, hence `TView.declTNode` is meaningless. // Falling back to `T_HOST` in case we cross component boundary. if (tView.type === 1 /* TViewType.Component */) { return lView[T_HOST]; } // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode. return null; } /** * This is a light weight version of the `enterView` which is needed by the DI system. * * @param lView `LView` location of the DI context. * @param tNode `TNode` for DI context * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration * tree from `tNode` until we find parent declared `TElementNode`. * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated * `NodeInjector` can be found and we should instead use `ModuleInjector`. * - If `true` than this call must be fallowed by `leaveDI` * - If `false` than this call failed and we should NOT call `leaveDI` */ function enterDI(lView, tNode, flags) { ngDevMode && assertLViewOrUndefined(lView); if (flags & InjectFlags.SkipSelf) { ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]); let parentTNode = tNode; let parentLView = lView; while (true) { ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined'); parentTNode = parentTNode.parent; if (parentTNode === null && !(flags & InjectFlags.Host)) { parentTNode = getDeclarationTNode(parentLView); if (parentTNode === null) break; // In this case, a parent exists and is definitely an element. So it will definitely // have an existing lView as the declaration view, which is why we can assume it's defined. ngDevMode && assertDefined(parentLView, 'Parent LView should be defined'); parentLView = parentLView[DECLARATION_VIEW]; // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives // We want to skip those and look only at Elements and ElementContainers to ensure // we're looking at true parent nodes, and not content or other types. if (parentTNode.type & (2 /* TNodeType.Element */ | 8 /* TNodeType.ElementContainer */)) { break; } } else { break; } } if (parentTNode === null) { // If we failed to find a parent TNode this means that we should use module injector. return false; } else { tNode = parentTNode; lView = parentLView; } } ngDevMode && assertTNodeForLView(tNode, lView); const lFrame = (instructionState.lFrame = allocLFrame()); lFrame.currentTNode = tNode; lFrame.lView = lView; return true; } /** * Swap the current lView with a new lView. * * For performance reasons we store the lView in the top level of the module. * This way we minimize the number of properties to read. Whenever a new view * is entered we have to store the lView for later, and when the view is * exited the state has to be restored * * @param newView New lView to become active * @returns the previously active lView; */ function enterView(newView) { ngDevMode && assertNotEqual(newView[0], newView[1], '????'); ngDevMode && assertLViewOrUndefined(newView); const newLFrame = allocLFrame(); if (ngDevMode) { assertEqual(newLFrame.isParent, true, 'Expected clean LFrame'); assertEqual(newLFrame.lView, null, 'Expected clean LFrame'); assertEqual(newLFrame.tView, null, 'Expected clean LFrame'); assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame'); assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame'); assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame'); } const tView = newView[TVIEW]; instructionState.lFrame = newLFrame; ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView); newLFrame.currentTNode = tView.firstChild; newLFrame.lView = newView; newLFrame.tView = tView; newLFrame.contextLView = newView; newLFrame.bindingIndex = tView.bindingStartIndex; newLFrame.inI18n = false; } /** * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure. */ function allocLFrame() { const currentLFrame = instructionState.lFrame; const childLFrame = currentLFrame === null ? null : currentLFrame.child; const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame; return newLFrame; } function createLFrame(parent) { const lFrame = { currentTNode: null, isParent: true, lView: null, tView: null, selectedIndex: -1, contextLView: null, elementDepthCount: 0, currentNamespace: null, currentDirectiveIndex: -1, bindingRootIndex: -1, bindingIndex: -1, currentQueryIndex: 0, parent: parent, child: null, inI18n: false, }; parent !== null && (parent.child = lFrame); // link the new LFrame for reuse. return lFrame; } /** * A lightweight version of leave which is used with DI. * * This function only resets `currentTNode` and `LView` as those are the only properties * used with DI (`enterDI()`). * * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`. */ function leaveViewLight() { const oldLFrame = instructionState.lFrame; instructionState.lFrame = oldLFrame.parent; oldLFrame.currentTNode = null; oldLFrame.lView = null; return oldLFrame; } /** * This is a lightweight version of the `leaveView` which is needed by the DI system. * * NOTE: this function is an alias so that we can change the type of the function to have `void` * return type. */ const leaveDI = leaveViewLight; /** * Leave the current `LView` * * This pops the `LFrame` with the associated `LView` from the stack. * * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is * because for performance reasons we don't release `LFrame` but rather keep it for next use. */ function leaveView() { const oldLFrame = leaveViewLight(); oldLFrame.isParent = true; oldLFrame.tView = null; oldLFrame.selectedIndex = -1; oldLFrame.contextLView = null; oldLFrame.elementDepthCount = 0; oldLFrame.currentDirectiveIndex = -1; oldLFrame.currentNamespace = null; oldLFrame.bindingRootIndex = -1; oldLFrame.bindingIndex = -1; oldLFrame.currentQueryIndex = 0; } function nextContextImpl(level) { const contextLView = (instructionState.lFrame.contextLView = walkUpViews(level, instructionState.lFrame.contextLView)); return contextLView[CONTEXT]; } /** * Gets the currently selected element index. * * Used with {@link property} instruction (and more in the future) to identify the index in the * current `LView` to act on. */ function getSelectedIndex() { return instructionState.lFrame.selectedIndex; } /** * Sets the most recent index passed to {@link select} * * Used with {@link property} instruction (and more in the future) to identify the index in the * current `LView` to act on. * * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be * run if and when the provided `index` value is different from the current selected index value.) */ function setSelectedIndex(index) { ngDevMode && index !== -1 && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).'); ngDevMode && assertLessThan(index, instructionState.lFrame.lView.length, "Can't set index passed end of LView"); instructionState.lFrame.selectedIndex = index; } /** * Gets the `tNode` that represents currently selected element. */ function getSelectedTNode() { const lFrame = instructionState.lFrame; return getTNode(lFrame.tView, lFrame.selectedIndex); } /** * Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state. * * @codeGenApi */ function ɵɵnamespaceSVG() { instructionState.lFrame.currentNamespace = SVG_NAMESPACE; } /** * Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state. * * @codeGenApi */ function ɵɵnamespaceMathML() { instructionState.lFrame.currentNamespace = MATH_ML_NAMESPACE; } /** * Sets the namespace used to create elements to `null`, which forces element creation to use * `createElement` rather than `createElementNS`. * * @codeGenApi */ function ɵɵnamespaceHTML() { namespaceHTMLInternal(); } /** * Sets the namespace used to create elements to `null`, which forces element creation to use * `createElement` rather than `createElementNS`. */ function namespaceHTMLInternal() { instructionState.lFrame.currentNamespace = null; } function getNamespace() { return instructionState.lFrame.currentNamespace; } let _wasLastNodeCreated = true; /** * Retrieves a global flag that indicates whether the most recent DOM node * was created or hydrated. */ function wasLastNodeCreated() { return _wasLastNodeCreated; } /** * Sets a global flag to indicate whether the most recent DOM node * was created or hydrated. */ function lastNodeWasCreated(flag) { _wasLastNodeCreated = flag; } /** * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`. * * Must be run *only* on the first template pass. * * Sets up the pre-order hooks on the provided `tView`, * see {@link HookData} for details about the data structure. * * @param directiveIndex The index of the directive in LView * @param directiveDef The definition containing the hooks to setup in tView * @param tView The current TView */ function registerPreOrderHooks(directiveIndex, directiveDef, tView) { ngDevMode && assertFirstCreatePass(tView); const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype; if (ngOnChanges) { const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef); (tView.preOrderHooks ??= []).push(directiveIndex, wrappedOnChanges); (tView.preOrderCheckHooks ??= []).push(directiveIndex, wrappedOnChanges); } if (ngOnInit) { (tView.preOrderHooks ??= []).push(0 - directiveIndex, ngOnInit); } if (ngDoCheck) { (tView.preOrderHooks ??= []).push(directiveIndex, ngDoCheck); (tView.preOrderCheckHooks ??= []).push(directiveIndex, ngDoCheck); } } /** * * Loops through the directives on the provided `tNode` and queues hooks to be * run that are not initialization hooks. * * Should be executed during `elementEnd()` and similar to * preserve hook execution order. Content, view, and destroy hooks for projected * components and directives must be called *before* their hosts. * * Sets up the content, view, and destroy hooks on the provided `tView`, * see {@link HookData} for details about the data structure. * * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up * separately at `elementStart`. * * @param tView The current TView * @param tNode The TNode whose directives are to be searched for hooks to queue */ function registerPostOrderHooks(tView, tNode) { ngDevMode && assertFirstCreatePass(tView); // It's necessary to loop through the directives at elementEnd() (rather than processing in // directiveCreate) so we can preserve the current hook order. Content, view, and destroy // hooks for projected components and directives must be called *before* their hosts. for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) { const directiveDef = tView.data[i]; ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef'); const lifecycleHooks = directiveDef.type.prototype; const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy, } = lifecycleHooks; if (ngAfterContentInit) { (tView.contentHooks ??= []).push(-i, ngAfterContentInit); } if (ngAfterContentChecked) { (tView.contentHooks ??= []).push(i, ngAfterContentChecked); (tView.contentCheckHooks ??= []).push(i, ngAfterContentChecked); } if (ngAfterViewInit) { (tView.viewHooks ??= []).push(-i, ngAfterViewInit); } if (ngAfterViewChecked) { (tView.viewHooks ??= []).push(i, ngAfterViewChecked); (tView.viewCheckHooks ??= []).push(i, ngAfterViewChecked); } if (ngOnDestroy != null) { (tView.destroyHooks ??= []).push(i, ngOnDestroy); } } } /** * Executing hooks requires complex logic as we need to deal with 2 constraints. * * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only * once, across many change detection cycles. This must be true even if some hooks throw, or if * some recursively trigger a change detection cycle. * To solve that, it is required to track the state of the execution of these init hooks. * This is done by storing and maintaining flags in the view: the {@link InitPhaseState}, * and the index within that phase. They can be seen as a cursor in the following structure: * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]] * They are stored as flags in LView[FLAGS]. * * 2. Pre-order hooks can be executed in batches, because of the select instruction. * To be able to pause and resume their execution, we also need some state about the hook's array * that is being processed: * - the index of the next hook to be executed * - the number of init hooks already found in the processed part of the array * They are stored as flags in LView[PREORDER_HOOK_FLAGS]. */ /** * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read * / write of the init-hooks related flags. * @param lView The LView where hooks are defined * @param hooks Hooks to be run * @param nodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function executeCheckHooks(lView, hooks, nodeIndex) { callHooks(lView, hooks, 3 /* InitPhaseState.InitPhaseCompleted */, nodeIndex); } /** * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked, * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed. * @param lView The LView where hooks are defined * @param hooks Hooks to be run * @param initPhase A phase for which hooks should be run * @param nodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) { ngDevMode && assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once'); if ((lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { callHooks(lView, hooks, initPhase, nodeIndex); } } function incrementInitPhaseFlags(lView, initPhase) { ngDevMode && assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.'); let flags = lView[FLAGS]; if ((flags & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { flags &= 16383 /* LViewFlags.IndexWithinInitPhaseReset */; flags += 1 /* LViewFlags.InitPhaseStateIncrementer */; lView[FLAGS] = flags; } } /** * Calls lifecycle hooks with their contexts, skipping init hooks if it's not * the first LView pass * * @param currentView The current view * @param arr The array in which the hooks are found * @param initPhaseState the current state of the init phase * @param currentNodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function callHooks(currentView, arr, initPhase, currentNodeIndex) { ngDevMode && assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.'); const startIndex = currentNodeIndex !== undefined ? currentView[PREORDER_HOOK_FLAGS] & 65535 /* PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask */ : 0; const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1; const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1 let lastNodeIndexFound = 0; for (let i = startIndex; i < max; i++) { const hook = arr[i + 1]; if (typeof hook === 'number') { lastNodeIndexFound = arr[i]; if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) { break; } } else { const isInitHook = arr[i] < 0; if (isInitHook) { currentView[PREORDER_HOOK_FLAGS] += 65536 /* PreOrderHookFlags.NumberOfInitHooksCalledIncrementer */; } if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) { callHook(currentView, initPhase, arr, i); currentView[PREORDER_HOOK_FLAGS] = (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* PreOrderHookFlags.NumberOfInitHooksCalledMask */) + i + 2; } i++; } } } /** * Executes a single lifecycle hook, making sure that: * - it is called in the non-reactive context; * - profiling data are registered. */ function callHookInternal(directive, hook) { profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook); const prevConsumer = setActiveConsumer$1(null); try { hook.call(directive); } finally { setActiveConsumer$1(prevConsumer); profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook); } } /** * Execute one hook against the current `LView`. * * @param currentView The current view * @param initPhaseState the current state of the init phase * @param arr The array in which the hooks are found * @param i The current index within the hook data array */ function callHook(currentView, initPhase, arr, i) { const isInitHook = arr[i] < 0; const hook = arr[i + 1]; const directiveIndex = isInitHook ? -arr[i] : arr[i]; const directive = currentView[directiveIndex]; if (isInitHook) { const indexWithintInitPhase = currentView[FLAGS] >> 14 /* LViewFlags.IndexWithinInitPhaseShift */; // The init phase state must be always checked here as it may have been recursively updated. if (indexWithintInitPhase < currentView[PREORDER_HOOK_FLAGS] >> 16 /* PreOrderHookFlags.NumberOfInitHooksCalledShift */ && (currentView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { currentView[FLAGS] += 16384 /* LViewFlags.IndexWithinInitPhaseIncrementer */; callHookInternal(directive, hook); } } else { callHookInternal(directive, hook); } } const NO_PARENT_INJECTOR = -1; /** * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in * `TView.data`. This allows us to store information about the current node's tokens (which * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be * shared, so they live in `LView`). * * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter * determines whether a directive is available on the associated node or not. This prevents us * from searching the directives array at this level unless it's probable the directive is in it. * * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters. * * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed * using interfaces as they were previously. The start index of each `LInjector` and `TInjector` * will differ based on where it is flattened into the main array, so it's not possible to know * the indices ahead of time and save their types here. The interfaces are still included here * for documentation purposes. * * export interface LInjector extends Array { * * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE) * [0]: number; * * // Cumulative bloom for directive IDs 32-63 * [1]: number; * * // Cumulative bloom for directive IDs 64-95 * [2]: number; * * // Cumulative bloom for directive IDs 96-127 * [3]: number; * * // Cumulative bloom for directive IDs 128-159 * [4]: number; * * // Cumulative bloom for directive IDs 160 - 191 * [5]: number; * * // Cumulative bloom for directive IDs 192 - 223 * [6]: number; * * // Cumulative bloom for directive IDs 224 - 255 * [7]: number; * * // We need to store a reference to the injector's parent so DI can keep looking up * // the injector tree until it finds the dependency it's looking for. * [PARENT_INJECTOR]: number; * } * * export interface TInjector extends Array { * * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE) * [0]: number; * * // Shared node bloom for directive IDs 32-63 * [1]: number; * * // Shared node bloom for directive IDs 64-95 * [2]: number; * * // Shared node bloom for directive IDs 96-127 * [3]: number; * * // Shared node bloom for directive IDs 128-159 * [4]: number; * * // Shared node bloom for directive IDs 160 - 191 * [5]: number; * * // Shared node bloom for directive IDs 192 - 223 * [6]: number; * * // Shared node bloom for directive IDs 224 - 255 * [7]: number; * * // Necessary to find directive indices for a particular node. * [TNODE]: TElementNode|TElementContainerNode|TContainerNode; * } */ /** * Factory for creating instances of injectors in the NodeInjector. * * This factory is complicated by the fact that it can resolve `multi` factories as well. * * NOTE: Some of the fields are optional which means that this class has two hidden classes. * - One without `multi` support (most common) * - One with `multi` values, (rare). * * Since VMs can cache up to 4 inline hidden classes this is OK. * * - Single factory: Only `resolving` and `factory` is defined. * - `providers` factory: `componentProviders` is a number and `index = -1`. * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`. */ class NodeInjectorFactory { factory; /** * The inject implementation to be activated when using the factory. */ injectImpl; /** * Marker set to true during factory invocation to see if we get into recursive loop. * Recursive loop causes an error to be displayed. */ resolving = false; /** * Marks that the token can see other Tokens declared in `viewProviders` on the same node. */ canSeeViewProviders; /** * An array of factories to use in case of `multi` provider. */ multi; /** * Number of `multi`-providers which belong to the component. * * This is needed because when multiple components and directives declare the `multi` provider * they have to be concatenated in the correct order. * * Example: * * If we have a component and directive active an a single element as declared here * ```ts * component: * providers: [ {provide: String, useValue: 'component', multi: true} ], * viewProviders: [ {provide: String, useValue: 'componentView', multi: true} ], * * directive: * providers: [ {provide: String, useValue: 'directive', multi: true} ], * ``` * * Then the expected results are: * * ```ts * providers: ['component', 'directive'] * viewProviders: ['component', 'componentView', 'directive'] * ``` * * The way to think about it is that the `viewProviders` have been inserted after the component * but before the directives, which is why we need to know how many `multi`s have been declared by * the component. */ componentProviders; /** * Current index of the Factory in the `data`. Needed for `viewProviders` and `providers` merging. * See `providerFactory`. */ index; /** * Because the same `multi` provider can be declared in `providers` and `viewProviders` it is * possible for `viewProviders` to shadow the `providers`. For this reason we store the * `provideFactory` of the `providers` so that `providers` can be extended with `viewProviders`. * * Example: * * Given: * ```ts * providers: [ {provide: String, useValue: 'all', multi: true} ], * viewProviders: [ {provide: String, useValue: 'viewOnly', multi: true} ], * ``` * * We have to return `['all']` in case of content injection, but `['all', 'viewOnly']` in case * of view injection. We further have to make sure that the shared instances (in our case * `all`) are the exact same instance in both the content as well as the view injection. (We * have to make sure that we don't double instantiate.) For this reason the `viewProviders` * `Factory` has a pointer to the shadowed `providers` factory so that it can instantiate the * `providers` (`['all']`) and then extend it with `viewProviders` (`['all'] + ['viewOnly'] = * ['all', 'viewOnly']`). */ providerFactory; constructor( /** * Factory to invoke in order to create a new instance. */ factory, /** * Set to `true` if the token is declared in `viewProviders` (or if it is component). */ isViewProvider, injectImplementation) { this.factory = factory; ngDevMode && assertDefined(factory, 'Factory not specified'); ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.'); this.canSeeViewProviders = isViewProvider; this.injectImpl = injectImplementation; } } /** * Converts `TNodeType` into human readable text. * Make sure this matches with `TNodeType` */ function toTNodeTypeAsString(tNodeType) { let text = ''; tNodeType & 1 /* TNodeType.Text */ && (text += '|Text'); tNodeType & 2 /* TNodeType.Element */ && (text += '|Element'); tNodeType & 4 /* TNodeType.Container */ && (text += '|Container'); tNodeType & 8 /* TNodeType.ElementContainer */ && (text += '|ElementContainer'); tNodeType & 16 /* TNodeType.Projection */ && (text += '|Projection'); tNodeType & 32 /* TNodeType.Icu */ && (text += '|IcuContainer'); tNodeType & 64 /* TNodeType.Placeholder */ && (text += '|Placeholder'); tNodeType & 128 /* TNodeType.LetDeclaration */ && (text += '|LetDeclaration'); return text.length > 0 ? text.substring(1) : text; } /** * Helper function to detect if a given value matches a `TNode` shape. * * The logic uses the `insertBeforeIndex` and its possible values as * a way to differentiate a TNode shape from other types of objects * within the `TView.data`. This is not a perfect check, but it can * be a reasonable differentiator, since we control the shapes of objects * within `TView.data`. */ function isTNodeShape(value) { return (value != null && typeof value === 'object' && (value.insertBeforeIndex === null || typeof value.insertBeforeIndex === 'number' || Array.isArray(value.insertBeforeIndex))); } function isLetDeclaration(tNode) { return !!(tNode.type & 128 /* TNodeType.LetDeclaration */); } /** * Returns `true` if the `TNode` has a directive which has `@Input()` for `class` binding. * * ```html *
* ``` * and * ```ts * @Directive({ * }) * class MyDirective { * @Input() * class: string; * } * ``` * * In the above case it is necessary to write the reconciled styling information into the * directive's input. * * @param tNode */ function hasClassInput(tNode) { return (tNode.flags & 8 /* TNodeFlags.hasClassInput */) !== 0; } /** * Returns `true` if the `TNode` has a directive which has `@Input()` for `style` binding. * * ```html *
* ``` * and * ```ts * @Directive({ * }) * class MyDirective { * @Input() * class: string; * } * ``` * * In the above case it is necessary to write the reconciled styling information into the * directive's input. * * @param tNode */ function hasStyleInput(tNode) { return (tNode.flags & 16 /* TNodeFlags.hasStyleInput */) !== 0; } function assertTNodeType(tNode, expectedTypes, message) { assertDefined(tNode, 'should be called with a TNode'); if ((tNode.type & expectedTypes) === 0) { throwError(message || `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`); } } function assertPureTNodeType(type) { if (!(type === 2 /* TNodeType.Element */ || type === 1 /* TNodeType.Text */ || type === 4 /* TNodeType.Container */ || type === 8 /* TNodeType.ElementContainer */ || type === 32 /* TNodeType.Icu */ || type === 16 /* TNodeType.Projection */ || type === 64 /* TNodeType.Placeholder */ || type === 128 /* TNodeType.LetDeclaration */)) { throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`); } } /** * Assigns all attribute values to the provided element via the inferred renderer. * * This function accepts two forms of attribute entries: * * default: (key, value): * attrs = [key1, value1, key2, value2] * * namespaced: (NAMESPACE_MARKER, uri, name, value) * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value] * * The `attrs` array can contain a mix of both the default and namespaced entries. * The "default" values are set without a marker, but if the function comes across * a marker value then it will attempt to set a namespaced value. If the marker is * not of a namespaced value then the function will quit and return the index value * where it stopped during the iteration of the attrs array. * * See [AttributeMarker] to understand what the namespace marker value is. * * Note that this instruction does not support assigning style and class values to * an element. See `elementStart` and `elementHostAttrs` to learn how styling values * are applied to an element. * @param renderer The renderer to be used * @param native The element that the attributes will be assigned to * @param attrs The attribute array of values that will be assigned to the element * @returns the index value that was last accessed in the attributes array */ function setUpAttributes(renderer, native, attrs) { let i = 0; while (i < attrs.length) { const value = attrs[i]; if (typeof value === 'number') { // only namespaces are supported. Other value types (such as style/class // entries) are not supported in this function. if (value !== 0 /* AttributeMarker.NamespaceURI */) { break; } // we just landed on the marker value ... therefore // we should skip to the next entry i++; const namespaceURI = attrs[i++]; const attrName = attrs[i++]; const attrVal = attrs[i++]; ngDevMode && ngDevMode.rendererSetAttribute++; renderer.setAttribute(native, attrName, attrVal, namespaceURI); } else { // attrName is string; const attrName = value; const attrVal = attrs[++i]; // Standard attributes ngDevMode && ngDevMode.rendererSetAttribute++; if (isAnimationProp(attrName)) { renderer.setProperty(native, attrName, attrVal); } else { renderer.setAttribute(native, attrName, attrVal); } i++; } } // another piece of code may iterate over the same attributes array. Therefore // it may be helpful to return the exact spot where the attributes array exited // whether by running into an unsupported marker or if all the static values were // iterated over. return i; } /** * Test whether the given value is a marker that indicates that the following * attribute values in a `TAttributes` array are only the names of attributes, * and not name-value pairs. * @param marker The attribute marker to test. * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`). */ function isNameOnlyAttributeMarker(marker) { return (marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ || marker === 6 /* AttributeMarker.I18n */); } function isAnimationProp(name) { // Perf note: accessing charCodeAt to check for the first character of a string is faster as // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that // charCodeAt doesn't allocate memory to return a substring. return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */; } /** * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process. * * This merge function keeps the order of attrs same. * * @param dst Location of where the merged `TAttributes` should end up. * @param src `TAttributes` which should be appended to `dst` */ function mergeHostAttrs(dst, src) { if (src === null || src.length === 0) ; else if (dst === null || dst.length === 0) { // We have source, but dst is empty, just make a copy. dst = src.slice(); } else { let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */; for (let i = 0; i < src.length; i++) { const item = src[i]; if (typeof item === 'number') { srcMarker = item; } else { if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) ; else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ || srcMarker === 2 /* AttributeMarker.Styles */) { // Case where we have to consume `key1` and `value` only. mergeHostAttribute(dst, srcMarker, item, null, src[++i]); } else { // Case where we have to consume `key1` only. mergeHostAttribute(dst, srcMarker, item, null, null); } } } } return dst; } /** * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account. * * @param dst `TAttributes` to append to. * @param marker Region where the `key`/`value` should be added. * @param key1 Key to add to `TAttributes` * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`) * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class. */ function mergeHostAttribute(dst, marker, key1, key2, value) { let i = 0; // Assume that new markers will be inserted at the end. let markerInsertPosition = dst.length; // scan until correct type. if (marker === -1 /* AttributeMarker.ImplicitAttributes */) { markerInsertPosition = -1; } else { while (i < dst.length) { const dstValue = dst[i++]; if (typeof dstValue === 'number') { if (dstValue === marker) { markerInsertPosition = -1; break; } else if (dstValue > marker) { // We need to save this as we want the markers to be inserted in specific order. markerInsertPosition = i - 1; break; } } } } // search until you find place of insertion while (i < dst.length) { const item = dst[i]; if (typeof item === 'number') { // since `i` started as the index after the marker, we did not find it if we are at the next // marker break; } else if (item === key1) { // We already have same token { if (value !== null) { dst[i + 1] = value; } return; } } // Increment counter. i++; if (value !== null) i++; } // insert at location. if (markerInsertPosition !== -1) { dst.splice(markerInsertPosition, 0, marker); i = markerInsertPosition + 1; } dst.splice(i++, 0, key1); if (value !== null) { dst.splice(i++, 0, value); } } /// Parent Injector Utils /////////////////////////////////////////////////////////////// function hasParentInjector(parentLocation) { return parentLocation !== NO_PARENT_INJECTOR; } function getParentInjectorIndex(parentLocation) { if (ngDevMode) { assertNumber(parentLocation, 'Number expected'); assertNotEqual(parentLocation, -1, 'Not a valid state.'); const parentInjectorIndex = parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */; assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.'); } return parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */; } function getParentInjectorViewOffset(parentLocation) { return parentLocation >> 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */; } /** * Unwraps a parent injector location number to find the view offset from the current injector, * then walks up the declaration view tree until the view is found that contains the parent * injector. * * @param location The location of the parent injector, which contains the view offset * @param startView The LView instance from which to start walking up the view tree * @returns The LView instance that contains the parent injector */ function getParentInjectorView(location, startView) { let viewOffset = getParentInjectorViewOffset(location); let parentView = startView; // For most cases, the parent injector can be found on the host node (e.g. for component // or container), but we must keep the loop here to support the rarer case of deeply nested // tags or inline views, where the parent injector might live many views // above the child injector. while (viewOffset > 0) { parentView = parentView[DECLARATION_VIEW]; viewOffset--; } return parentView; } /** * Defines if the call to `inject` should include `viewProviders` in its resolution. * * This is set to true when we try to instantiate a component. This value is reset in * `getNodeInjectable` to a value which matches the declaration location of the token about to be * instantiated. This is done so that if we are injecting a token which was declared outside of * `viewProviders` we don't accidentally pull `viewProviders` in. * * Example: * * ```ts * @Injectable() * class MyService { * constructor(public value: String) {} * } * * @Component({ * providers: [ * MyService, * {provide: String, value: 'providers' } * ] * viewProviders: [ * {provide: String, value: 'viewProviders'} * ] * }) * class MyComponent { * constructor(myService: MyService, value: String) { * // We expect that Component can see into `viewProviders`. * expect(value).toEqual('viewProviders'); * // `MyService` was not declared in `viewProviders` hence it can't see it. * expect(myService.value).toEqual('providers'); * } * } * * ``` */ let includeViewProviders = true; function setIncludeViewProviders(v) { const oldValue = includeViewProviders; includeViewProviders = v; return oldValue; } /** * The number of slots in each bloom filter (used by DI). The larger this number, the fewer * directives that will share slots, and thus, the fewer false positives when checking for * the existence of a directive. */ const BLOOM_SIZE = 256; const BLOOM_MASK = BLOOM_SIZE - 1; /** * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits, * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash * number. */ const BLOOM_BUCKET_BITS = 5; /** Counter used to generate unique IDs for directives. */ let nextNgElementId = 0; /** Value used when something wasn't found by an injector. */ const NOT_FOUND = {}; /** * Registers this directive as present in its node's injector by flipping the directive's * corresponding bit in the injector's bloom filter. * * @param injectorIndex The index of the node injector where this token should be registered * @param tView The TView for the injector's bloom filters * @param type The directive token to register */ function bloomAdd(injectorIndex, tView, type) { ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true'); let id; if (typeof type === 'string') { id = type.charCodeAt(0) || 0; } else if (type.hasOwnProperty(NG_ELEMENT_ID)) { id = type[NG_ELEMENT_ID]; } // Set a unique ID on the directive type, so if something tries to inject the directive, // we can easily retrieve the ID and hash it into the bloom bit that should be checked. if (id == null) { id = type[NG_ELEMENT_ID] = nextNgElementId++; } // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each), // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter. const bloomHash = id & BLOOM_MASK; // Create a mask that targets the specific bit associated with the directive. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding // to bit positions 0 - 31 in a 32 bit integer. const mask = 1 << bloomHash; // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`. // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask // should be written to. tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask; } /** * Creates (or gets an existing) injector for a given element or container. * * @param tNode for which an injector should be retrieved / created. * @param lView View where the node is stored * @returns Node injector */ function getOrCreateNodeInjectorForNode(tNode, lView) { const existingInjectorIndex = getInjectorIndex(tNode, lView); if (existingInjectorIndex !== -1) { return existingInjectorIndex; } const tView = lView[TVIEW]; if (tView.firstCreatePass) { tNode.injectorIndex = lView.length; insertBloom(tView.data, tNode); // foundation for node bloom insertBloom(lView, null); // foundation for cumulative bloom insertBloom(tView.blueprint, null); } const parentLoc = getParentInjectorLocation(tNode, lView); const injectorIndex = tNode.injectorIndex; // If a parent injector can't be found, its location is set to -1. // In that case, we don't need to set up a cumulative bloom if (hasParentInjector(parentLoc)) { const parentIndex = getParentInjectorIndex(parentLoc); const parentLView = getParentInjectorView(parentLoc, lView); const parentData = parentLView[TVIEW].data; // Creates a cumulative bloom filter that merges the parent's bloom filter // and its own cumulative bloom (which contains tokens for all ancestors) for (let i = 0; i < 8 /* NodeInjectorOffset.BLOOM_SIZE */; i++) { lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i]; } } lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */] = parentLoc; return injectorIndex; } function insertBloom(arr, footer) { arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer); } function getInjectorIndex(tNode, lView) { if (tNode.injectorIndex === -1 || // If the injector index is the same as its parent's injector index, then the index has been // copied down from the parent node. No injector has been created yet on this node. (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) || // After the first template pass, the injector index might exist but the parent values // might not have been calculated yet for this instance lView[tNode.injectorIndex + 8 /* NodeInjectorOffset.PARENT */] === null) { return -1; } else { ngDevMode && assertIndexInRange(lView, tNode.injectorIndex); return tNode.injectorIndex; } } /** * Finds the index of the parent injector, with a view offset if applicable. Used to set the * parent injector initially. * * @returns Returns a number that is the combination of the number of LViews that we have to go up * to find the LView containing the parent inject AND the index of the injector within that LView. */ function getParentInjectorLocation(tNode, lView) { if (tNode.parent && tNode.parent.injectorIndex !== -1) { // If we have a parent `TNode` and there is an injector associated with it we are done, because // the parent injector is within the current `LView`. return tNode.parent.injectorIndex; // ViewOffset is 0 } // When parent injector location is computed it may be outside of the current view. (ie it could // be pointing to a declared parent location). This variable stores number of declaration parents // we need to walk up in order to find the parent injector location. let declarationViewOffset = 0; let parentTNode = null; let lViewCursor = lView; // The parent injector is not in the current `LView`. We will have to walk the declared parent // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent // `NodeInjector`. while (lViewCursor !== null) { parentTNode = getTNodeFromLView(lViewCursor); if (parentTNode === null) { // If we have no parent, than we are done. return NO_PARENT_INJECTOR; } ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]); // Every iteration of the loop requires that we go to the declared parent. declarationViewOffset++; lViewCursor = lViewCursor[DECLARATION_VIEW]; if (parentTNode.injectorIndex !== -1) { // We found a NodeInjector which points to something. return (parentTNode.injectorIndex | (declarationViewOffset << 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */)); } } return NO_PARENT_INJECTOR; } /** * Makes a type or an injection token public to the DI system by adding it to an * injector's bloom filter. * * @param di The node injector in which a directive will be added * @param token The type or the injection token to be made public */ function diPublicInInjector(injectorIndex, tView, token) { bloomAdd(injectorIndex, tView, token); } /** * Inject static attribute value into directive constructor. * * This method is used with `factory` functions which are generated as part of * `defineDirective` or `defineComponent`. The method retrieves the static value * of an attribute. (Dynamic attributes are not supported since they are not resolved * at the time of injection and can change over time.) * * # Example * Given: * ```ts * @Component(...) * class MyComponent { * constructor(@Attribute('title') title: string) { ... } * } * ``` * When instantiated with * ```html * * ``` * * Then factory method generated is: * ```ts * MyComponent.ɵcmp = defineComponent({ * factory: () => new MyComponent(injectAttribute('title')) * ... * }) * ``` * * @publicApi */ function injectAttributeImpl(tNode, attrNameToInject) { ngDevMode && assertTNodeType(tNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */); ngDevMode && assertDefined(tNode, 'expecting tNode'); if (attrNameToInject === 'class') { return tNode.classes; } if (attrNameToInject === 'style') { return tNode.styles; } const attrs = tNode.attrs; if (attrs) { const attrsLength = attrs.length; let i = 0; while (i < attrsLength) { const value = attrs[i]; // If we hit a `Bindings` or `Template` marker then we are done. if (isNameOnlyAttributeMarker(value)) break; // Skip namespaced attributes if (value === 0 /* AttributeMarker.NamespaceURI */) { // we skip the next two values // as namespaced attributes looks like // [..., AttributeMarker.NamespaceURI, 'http://someuri.com/test', 'test:exist', // 'existValue', ...] i = i + 2; } else if (typeof value === 'number') { // Skip to the first value of the marked attribute. i++; while (i < attrsLength && typeof attrs[i] === 'string') { i++; } } else if (value === attrNameToInject) { return attrs[i + 1]; } else { i = i + 2; } } } return null; } function notFoundValueOrThrow(notFoundValue, token, flags) { if (flags & InjectFlags.Optional || notFoundValue !== undefined) { return notFoundValue; } else { throwProviderNotFoundError(token, 'NodeInjector'); } } /** * Returns the value associated to the given token from the ModuleInjector or throws exception * * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector or throws an exception */ function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) { if (flags & InjectFlags.Optional && notFoundValue === undefined) { // This must be set or the NullInjector will throw for optional deps notFoundValue = null; } if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) { const moduleInjector = lView[INJECTOR]; // switch to `injectInjectorOnly` implementation for module injector, since module injector // should not have access to Component/Directive DI scope (that may happen through // `directiveInject` implementation) const previousInjectImplementation = setInjectImplementation(undefined); try { if (moduleInjector) { return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional); } else { return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional); } } finally { setInjectImplementation(previousInjectImplementation); } } return notFoundValueOrThrow(notFoundValue, token, flags); } /** * Returns the value associated to the given token from the NodeInjectors => ModuleInjector. * * Look for the injector providing the token by walking up the node injector tree and then * the module injector tree. * * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`) * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) { if (tNode !== null) { // If the view or any of its ancestors have an embedded // view injector, we have to look it up there first. if (lView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */ && // The token must be present on the current node injector when the `Self` // flag is set, so the lookup on embedded view injector(s) can be skipped. !(flags & InjectFlags.Self)) { const embeddedInjectorValue = lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, NOT_FOUND); if (embeddedInjectorValue !== NOT_FOUND) { return embeddedInjectorValue; } } // Otherwise try the node injector. const value = lookupTokenUsingNodeInjector(tNode, lView, token, flags, NOT_FOUND); if (value !== NOT_FOUND) { return value; } } // Finally, fall back to the module injector. return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue); } /** * Returns the value associated to the given token from the node injector. * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function lookupTokenUsingNodeInjector(tNode, lView, token, flags, notFoundValue) { const bloomHash = bloomHashBitOrFactory(token); // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef // so just call the factory function to create it. if (typeof bloomHash === 'function') { if (!enterDI(lView, tNode, flags)) { // Failed to enter DI, try module injector instead. If a token is injected with the @Host // flag, the module injector is not searched for that token in Ivy. return flags & InjectFlags.Host ? notFoundValueOrThrow(notFoundValue, token, flags) : lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue); } try { let value; if (ngDevMode) { runInInjectorProfilerContext(new NodeInjector(getCurrentTNode(), getLView()), token, () => { value = bloomHash(flags); if (value != null) { emitInstanceCreatedByInjectorEvent(value); } }); } else { value = bloomHash(flags); } if (value == null && !(flags & InjectFlags.Optional)) { throwProviderNotFoundError(token); } else { return value; } } finally { leaveDI(); } } else if (typeof bloomHash === 'number') { // A reference to the previous injector TView that was found while climbing the element // injector tree. This is used to know if viewProviders can be accessed on the current // injector. let previousTView = null; let injectorIndex = getInjectorIndex(tNode, lView); let parentLocation = NO_PARENT_INJECTOR; let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null; // If we should skip this injector, or if there is no injector on this node, start by // searching the parent injector. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) { parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) : lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */]; if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) { injectorIndex = -1; } else { previousTView = lView[TVIEW]; injectorIndex = getParentInjectorIndex(parentLocation); lView = getParentInjectorView(parentLocation, lView); } } // Traverse up the injector tree until we find a potential match or until we know there // *isn't* a match. while (injectorIndex !== -1) { ngDevMode && assertNodeInjector(lView, injectorIndex); // Check the current injector. If it matches, see if it contains token. const tView = lView[TVIEW]; ngDevMode && assertTNodeForLView(tView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */], lView); if (bloomHasToken(bloomHash, injectorIndex, tView.data)) { // At this point, we have an injector which *may* contain the token, so we step through // the providers and directives associated with the injector's corresponding node to get // the instance. const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode); if (instance !== NOT_FOUND) { return instance; } } parentLocation = lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */]; if (parentLocation !== NO_PARENT_INJECTOR && shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */] === hostTElementNode) && bloomHasToken(bloomHash, injectorIndex, lView)) { // The def wasn't found anywhere on this node, so it was a false positive. // Traverse up the tree and continue searching. previousTView = tView; injectorIndex = getParentInjectorIndex(parentLocation); lView = getParentInjectorView(parentLocation, lView); } else { // If we should not search parent OR If the ancestor bloom filter value does not have the // bit corresponding to the directive we can give up on traversing up to find the specific // injector. injectorIndex = -1; } } } return notFoundValue; } function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) { const currentTView = lView[TVIEW]; const tNode = currentTView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */]; // First, we need to determine if view providers can be accessed by the starting element. // There are two possibilities const canAccessViewProviders = previousTView == null ? // 1) This is the first invocation `previousTView == null` which means that we are at the // `TNode` of where injector is starting to look. In such a case the only time we are allowed // to look into the ViewProviders is if: // - we are on a component // - AND the injector set `includeViewProviders` to true (implying that the token can see // ViewProviders because it is the Component or a Service which itself was declared in // ViewProviders) isComponentHost(tNode) && includeViewProviders : // 2) `previousTView != null` which means that we are now walking across the parent nodes. // In such a case we are only allowed to look into the ViewProviders if: // - We just crossed from child View to Parent View `previousTView != currentTView` // - AND the parent TNode is an Element. // This means that we just came from the Component's View and therefore are allowed to see // into the ViewProviders. previousTView != currentTView && (tNode.type & 3 /* TNodeType.AnyRNode */) !== 0; // This special case happens when there is a @host on the inject and when we are searching // on the host element node. const isHostSpecialCase = flags & InjectFlags.Host && hostTElementNode === tNode; const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase); if (injectableIdx !== null) { return getNodeInjectable(lView, currentTView, injectableIdx, tNode, flags); } else { return NOT_FOUND; } } /** * Searches for the given token among the node's directives and providers. * * @param tNode TNode on which directives are present. * @param tView The tView we are currently processing * @param token Provider token or type of a directive to look for. * @param canAccessViewProviders Whether view providers should be considered. * @param isHostSpecialCase Whether the host special case applies. * @returns Index of a found directive or provider, or null when none found. */ function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) { const nodeProviderIndexes = tNode.providerIndexes; const tInjectables = tView.data; const injectablesStart = nodeProviderIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */; const directivesStart = tNode.directiveStart; const directiveEnd = tNode.directiveEnd; const cptViewProvidersCount = nodeProviderIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */; const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount; // When the host special case applies, only the viewProviders and the component are visible const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd; for (let i = startingIndex; i < endIndex; i++) { const providerTokenOrDef = tInjectables[i]; if ((i < directivesStart && token === providerTokenOrDef) || (i >= directivesStart && providerTokenOrDef.type === token)) { return i; } } if (isHostSpecialCase) { const dirDef = tInjectables[directivesStart]; if (dirDef && isComponentDef(dirDef) && dirDef.type === token) { return directivesStart; } } return null; } /** * Retrieve or instantiate the injectable from the `LView` at particular `index`. * * This function checks to see if the value has already been instantiated and if so returns the * cached `injectable`. Otherwise if it detects that the value is still a factory it * instantiates the `injectable` and caches the value. */ function getNodeInjectable(lView, tView, index, tNode, flags) { let value = lView[index]; const tData = tView.data; if (value instanceof NodeInjectorFactory) { const factory = value; if (factory.resolving) { throwCyclicDependencyError(stringifyForError(tData[index])); } const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders); factory.resolving = true; let prevInjectContext; if (ngDevMode) { // tData indexes mirror the concrete instances in its corresponding LView. // lView[index] here is either the injectable instace itself or a factory, // therefore tData[index] is the constructor of that injectable or a // definition object that contains the constructor in a `.type` field. const token = tData[index].type || tData[index]; const injector = new NodeInjector(tNode, lView); prevInjectContext = setInjectorProfilerContext({ injector, token }); } const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null; const success = enterDI(lView, tNode, InjectFlags.Default); ngDevMode && assertEqual(success, true, "Because flags do not contain `SkipSelf' we expect this to always succeed."); try { value = lView[index] = factory.factory(undefined, flags, tData, lView, tNode); ngDevMode && emitInstanceCreatedByInjectorEvent(value); // This code path is hit for both directives and providers. // For perf reasons, we want to avoid searching for hooks on providers. // It does no harm to try (the hooks just won't exist), but the extra // checks are unnecessary and this is a hot path. So we check to see // if the index of the dependency is in the directive range for this // tNode. If it's not, we know it's a provider and skip hook registration. if (tView.firstCreatePass && index >= tNode.directiveStart) { ngDevMode && assertDirectiveDef(tData[index]); registerPreOrderHooks(index, tData[index], tView); } } finally { ngDevMode && setInjectorProfilerContext(prevInjectContext); previousInjectImplementation !== null && setInjectImplementation(previousInjectImplementation); setIncludeViewProviders(previousIncludeViewProviders); factory.resolving = false; leaveDI(); } } return value; } /** * Returns the bit in an injector's bloom filter that should be used to determine whether or not * the directive might be provided by the injector. * * When a directive is public, it is added to the bloom filter and given a unique ID that can be * retrieved on the Type. When the directive isn't public or the token is not a directive `null` * is returned as the node injector can not possibly provide that token. * * @param token the injection token * @returns the matching bit to check in the bloom filter or `null` if the token is not known. * When the returned value is negative then it represents special values such as `Injector`. */ function bloomHashBitOrFactory(token) { ngDevMode && assertDefined(token, 'token must be defined'); if (typeof token === 'string') { return token.charCodeAt(0) || 0; } const tokenId = // First check with `hasOwnProperty` so we don't get an inherited ID. token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined; // Negative token IDs are used for special objects such as `Injector` if (typeof tokenId === 'number') { if (tokenId >= 0) { return tokenId & BLOOM_MASK; } else { ngDevMode && assertEqual(tokenId, -1 /* InjectorMarkers.Injector */, 'Expecting to get Special Injector Id'); return createNodeInjector; } } else { return tokenId; } } function bloomHasToken(bloomHash, injectorIndex, injectorView) { // Create a mask that targets the specific bit associated with the directive we're looking for. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding // to bit positions 0 - 31 in a 32 bit integer. const mask = 1 << bloomHash; // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset // that should be used. const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)]; // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on, // this injector is a potential match. return !!(value & mask); } /** Returns true if flags prevent parent injector from being searched for tokens */ function shouldSearchParent(flags, isFirstHostTNode) { return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode); } function getNodeInjectorLView(nodeInjector) { return nodeInjector._lView; } function getNodeInjectorTNode(nodeInjector) { return nodeInjector._tNode; } class NodeInjector { _tNode; _lView; constructor(_tNode, _lView) { this._tNode = _tNode; this._lView = _lView; } get(token, notFoundValue, flags) { return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue); } } /** Creates a `NodeInjector` for the current node. */ function createNodeInjector() { return new NodeInjector(getCurrentTNode(), getLView()); } /** * @codeGenApi */ function ɵɵgetInheritedFactory(type) { return noSideEffects(() => { const ownConstructor = type.prototype.constructor; const ownFactory = ownConstructor[NG_FACTORY_DEF] || getFactoryOf(ownConstructor); const objectPrototype = Object.prototype; let parent = Object.getPrototypeOf(type.prototype).constructor; // Go up the prototype until we hit `Object`. while (parent && parent !== objectPrototype) { const factory = parent[NG_FACTORY_DEF] || getFactoryOf(parent); // If we hit something that has a factory and the factory isn't the same as the type, // we've found the inherited factory. Note the check that the factory isn't the type's // own factory is redundant in most cases, but if the user has custom decorators on the // class, this lookup will start one level down in the prototype chain, causing us to // find the own factory first and potentially triggering an infinite loop downstream. if (factory && factory !== ownFactory) { return factory; } parent = Object.getPrototypeOf(parent); } // There is no factory defined. Either this was improper usage of inheritance // (no Angular decorator on the superclass) or there is no constructor at all // in the inheritance chain. Since the two cases cannot be distinguished, the // latter has to be assumed. return (t) => new t(); }); } function getFactoryOf(type) { if (isForwardRef(type)) { return () => { const factory = getFactoryOf(resolveForwardRef(type)); return factory && factory(); }; } return getFactoryDef(type); } /** * Returns a value from the closest embedded or node injector. * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, notFoundValue) { let currentTNode = tNode; let currentLView = lView; // When an LView with an embedded view injector is inserted, it'll likely be interlaced with // nodes who may have injectors (e.g. node injector -> embedded view injector -> node injector). // Since the bloom filters for the node injectors have already been constructed and we don't // have a way of extracting the records from an injector, the only way to maintain the correct // hierarchy when resolving the value is to walk it node-by-node while attempting to resolve // the token at each level. while (currentTNode !== null && currentLView !== null && currentLView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */ && !isRootView(currentLView)) { ngDevMode && assertTNodeForLView(currentTNode, currentLView); // Note that this lookup on the node injector is using the `Self` flag, because // we don't want the node injector to look at any parent injectors since we // may hit the embedded view injector first. const nodeInjectorValue = lookupTokenUsingNodeInjector(currentTNode, currentLView, token, flags | InjectFlags.Self, NOT_FOUND); if (nodeInjectorValue !== NOT_FOUND) { return nodeInjectorValue; } // Has an explicit type due to a TS bug: https://github.com/microsoft/TypeScript/issues/33191 let parentTNode = currentTNode.parent; // `TNode.parent` includes the parent within the current view only. If it doesn't exist, // it means that we've hit the view boundary and we need to go up to the next view. if (!parentTNode) { // Before we go to the next LView, check if the token exists on the current embedded injector. const embeddedViewInjector = currentLView[EMBEDDED_VIEW_INJECTOR]; if (embeddedViewInjector) { const embeddedViewInjectorValue = embeddedViewInjector.get(token, NOT_FOUND, flags); if (embeddedViewInjectorValue !== NOT_FOUND) { return embeddedViewInjectorValue; } } // Otherwise keep going up the tree. parentTNode = getTNodeFromLView(currentLView); currentLView = currentLView[DECLARATION_VIEW]; } currentTNode = parentTNode; } return notFoundValue; } /** Gets the TNode associated with an LView inside of the declaration view. */ function getTNodeFromLView(lView) { const tView = lView[TVIEW]; const tViewType = tView.type; // The parent pointer differs based on `TView.type`. if (tViewType === 2 /* TViewType.Embedded */) { ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.'); return tView.declTNode; } else if (tViewType === 1 /* TViewType.Component */) { // Components don't have `TView.declTNode` because each instance of component could be // inserted in different location, hence `TView.declTNode` is meaningless. return lView[T_HOST]; } return null; } /** * Facade for the attribute injection from DI. * * @codeGenApi */ function ɵɵinjectAttribute(attrNameToInject) { return injectAttributeImpl(getCurrentTNode(), attrNameToInject); } /** * Attribute decorator and metadata. * * @Annotation * @publicApi */ const Attribute = makeParamDecorator('Attribute', (attributeName) => ({ attributeName, __NG_ELEMENT_ID__: () => ɵɵinjectAttribute(attributeName), })); let _reflect = null; function getReflect() { return (_reflect = _reflect || new ReflectionCapabilities()); } function reflectDependencies(type) { return convertDependencies(getReflect().parameters(type)); } function convertDependencies(deps) { return deps.map((dep) => reflectDependency(dep)); } function reflectDependency(dep) { const meta = { token: null, attribute: null, host: false, optional: false, self: false, skipSelf: false, }; if (Array.isArray(dep) && dep.length > 0) { for (let j = 0; j < dep.length; j++) { const param = dep[j]; if (param === undefined) { // param may be undefined if type of dep is not set by ngtsc continue; } const proto = Object.getPrototypeOf(param); if (param instanceof Optional || proto.ngMetadataName === 'Optional') { meta.optional = true; } else if (param instanceof SkipSelf || proto.ngMetadataName === 'SkipSelf') { meta.skipSelf = true; } else if (param instanceof Self || proto.ngMetadataName === 'Self') { meta.self = true; } else if (param instanceof Host || proto.ngMetadataName === 'Host') { meta.host = true; } else if (param instanceof Inject) { meta.token = param.token; } else if (param instanceof Attribute) { if (param.attributeName === undefined) { throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Attribute name must be defined.`); } meta.attribute = param.attributeName; } else { meta.token = param; } } } else if (dep === undefined || (Array.isArray(dep) && dep.length === 0)) { meta.token = null; } else { meta.token = dep; } return meta; } /** * Compile an Angular injectable according to its `Injectable` metadata, and patch the resulting * injectable def (`ɵprov`) onto the injectable type. */ function compileInjectable(type, meta) { let ngInjectableDef = null; let ngFactoryDef = null; // if NG_PROV_DEF is already defined on this class then don't overwrite it if (!type.hasOwnProperty(NG_PROV_DEF)) { Object.defineProperty(type, NG_PROV_DEF, { get: () => { if (ngInjectableDef === null) { const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type, }); ngInjectableDef = compiler.compileInjectable(angularCoreDiEnv, `ng:///${type.name}/ɵprov.js`, getInjectableMetadata(type, meta)); } return ngInjectableDef; }, }); } // if NG_FACTORY_DEF is already defined on this class then don't overwrite it if (!type.hasOwnProperty(NG_FACTORY_DEF)) { Object.defineProperty(type, NG_FACTORY_DEF, { get: () => { if (ngFactoryDef === null) { const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type, }); ngFactoryDef = compiler.compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, { name: type.name, type, typeArgumentCount: 0, // In JIT mode types are not available nor used. deps: reflectDependencies(type), target: compiler.FactoryTarget.Injectable, }); } return ngFactoryDef; }, // Leave this configurable so that the factories from directives or pipes can take precedence. configurable: true, }); } } const USE_VALUE = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty, }); function isUseClassProvider(meta) { return meta.useClass !== undefined; } function isUseValueProvider(meta) { return USE_VALUE in meta; } function isUseFactoryProvider(meta) { return meta.useFactory !== undefined; } function isUseExistingProvider(meta) { return meta.useExisting !== undefined; } function getInjectableMetadata(type, srcMeta) { // Allow the compilation of a class with a `@Injectable()` decorator without parameters const meta = srcMeta || { providedIn: null }; const compilerMeta = { name: type.name, type: type, typeArgumentCount: 0, providedIn: meta.providedIn, }; if ((isUseClassProvider(meta) || isUseFactoryProvider(meta)) && meta.deps !== undefined) { compilerMeta.deps = convertDependencies(meta.deps); } // Check to see if the user explicitly provided a `useXxxx` property. if (isUseClassProvider(meta)) { compilerMeta.useClass = meta.useClass; } else if (isUseValueProvider(meta)) { compilerMeta.useValue = meta.useValue; } else if (isUseFactoryProvider(meta)) { compilerMeta.useFactory = meta.useFactory; } else if (isUseExistingProvider(meta)) { compilerMeta.useExisting = meta.useExisting; } return compilerMeta; } /** * Injectable decorator and metadata. * * @Annotation * @publicApi */ const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, (type, meta) => compileInjectable(type, meta)); /** * Create a new `Injector` which is configured using a `defType` of `InjectorType`s. */ function createInjector(defType, parent = null, additionalProviders = null, name) { const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name); injector.resolveInjectorInitializers(); return injector; } /** * Creates a new injector without eagerly resolving its injector types. Can be used in places * where resolving the injector types immediately can lead to an infinite loop. The injector types * should be resolved at a later point by calling `_resolveInjectorDefTypes`. */ function createInjectorWithoutInjectorInstances(defType, parent = null, additionalProviders = null, name, scopes = new Set()) { const providers = [additionalProviders || EMPTY_ARRAY, importProvidersFrom(defType)]; name = name || (typeof defType === 'object' ? undefined : stringify(defType)); return new R3Injector(providers, parent || getNullInjector(), name || null, scopes); } /** * Concrete injectors implement this interface. Injectors are configured * with [providers](guide/di/dependency-injection-providers) that associate * dependencies of various types with [injection tokens](guide/di/dependency-injection-providers). * * @see [DI Providers](guide/di/dependency-injection-providers). * @see {@link StaticProvider} * * @usageNotes * * The following example creates a service injector instance. * * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'} * * ### Usage example * * {@example core/di/ts/injector_spec.ts region='Injector'} * * `Injector` returns itself when given `Injector` as a token: * * {@example core/di/ts/injector_spec.ts region='injectInjector'} * * @publicApi */ class Injector { static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND; static NULL = new NullInjector(); static create(options, parent) { if (Array.isArray(options)) { return createInjector({ name: '' }, parent, options, ''); } else { const name = options.name ?? ''; return createInjector({ name }, options.parent, options.providers, name); } } /** @nocollapse */ static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({ token: Injector, providedIn: 'any', factory: () => ɵɵinject(INJECTOR$1), }); /** * @internal * @nocollapse */ static __NG_ELEMENT_ID__ = -1 /* InjectorMarkers.Injector */; } /*! * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ /** * Creates a token that can be used to inject static attributes of the host node. * * @usageNotes * ### Injecting an attribute that is known to exist * ```ts * @Directive() * class MyDir { * attr: string = inject(new HostAttributeToken('some-attr')); * } * ``` * * ### Optionally injecting an attribute * ```ts * @Directive() * class MyDir { * attr: string | null = inject(new HostAttributeToken('some-attr'), {optional: true}); * } * ``` * @publicApi */ class HostAttributeToken { attributeName; constructor(attributeName) { this.attributeName = attributeName; } /** @internal */ __NG_ELEMENT_ID__ = () => ɵɵinjectAttribute(this.attributeName); toString() { return `HostAttributeToken ${this.attributeName}`; } } /** * A token that can be used to inject the tag name of the host node. * * @usageNotes * ### Injecting a tag name that is known to exist * ```ts * @Directive() * class MyDir { * tagName: string = inject(HOST_TAG_NAME); * } * ``` * * ### Optionally injecting a tag name * ```ts * @Directive() * class MyDir { * tagName: string | null = inject(HOST_TAG_NAME, {optional: true}); * } * ``` * @publicApi */ const HOST_TAG_NAME = new InjectionToken(ngDevMode ? 'HOST_TAG_NAME' : ''); // HOST_TAG_NAME should be resolved at the current node, similar to e.g. ElementRef, // so we manually specify __NG_ELEMENT_ID__ here, instead of using a factory. // tslint:disable-next-line:no-toplevel-property-access HOST_TAG_NAME.__NG_ELEMENT_ID__ = (flags) => { const tNode = getCurrentTNode(); if (tNode === null) { throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'HOST_TAG_NAME can only be injected in directives and components ' + 'during construction time (in a class constructor or as a class field initializer)'); } if (tNode.type & 2 /* TNodeType.Element */) { return tNode.value; } if (flags & InjectFlags.Optional) { return null; } throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `HOST_TAG_NAME was used on ${getDevModeNodeName(tNode)} which doesn't have an underlying element in the DOM. ` + `This is invalid, and so the dependency should be marked as optional.`); }; function getDevModeNodeName(tNode) { if (tNode.type & 8 /* TNodeType.ElementContainer */) { return 'an '; } else if (tNode.type & 4 /* TNodeType.Container */) { return 'an '; } else if (tNode.type & 128 /* TNodeType.LetDeclaration */) { return 'an @let declaration'; } else { return 'a node'; } } const SCHEDULE_IN_ROOT_ZONE_DEFAULT = false; /** * `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior. * The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef` * is injected in a component or directive, the callbacks run when that component or * directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed. * * @publicApi */ class DestroyRef { /** * @internal * @nocollapse */ static __NG_ELEMENT_ID__ = injectDestroyRef; /** * @internal * @nocollapse */ static __NG_ENV_ID__ = (injector) => injector; } class NodeInjectorDestroyRef extends DestroyRef { _lView; constructor(_lView) { super(); this._lView = _lView; } onDestroy(callback) { const lView = this._lView; // Checking if `lView` is already destroyed before storing the `callback` enhances // safety and integrity for applications. // If `lView` is destroyed, we call the `callback` immediately to ensure that // any necessary cleanup is handled gracefully. // With this approach, we're providing better reliability in managing resources. // One of the use cases is `takeUntilDestroyed`, which aims to replace `takeUntil` // in existing applications. While `takeUntil` can be safely called once the view // is destroyed — resulting in no errors and finalizing the subscription depending // on whether a subject or replay subject is used, replacing it with // `takeUntilDestroyed` introduces a breaking change, as it throws an error if // the `lView` is destroyed (https://github.com/angular/angular/issues/54527). if (isDestroyed(lView)) { callback(); // We return a "noop" callback, which, when executed, does nothing because // we haven't stored anything on the `lView`, and thus there's nothing to remove. return () => { }; } storeLViewOnDestroy(lView, callback); return () => removeLViewOnDestroy(lView, callback); } } function injectDestroyRef() { return new NodeInjectorDestroyRef(getLView()); } /** * Injectable that is notified when an `LView` is made aware of changes to application state. */ class ChangeDetectionScheduler { } /** Token used to indicate if zoneless was enabled via provideZonelessChangeDetection(). */ const ZONELESS_ENABLED = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'Zoneless enabled' : '', { providedIn: 'root', factory: () => false }); /** Token used to indicate `provideExperimentalZonelessChangeDetection` was used. */ const PROVIDED_ZONELESS = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'Zoneless provided' : '', { providedIn: 'root', factory: () => false }); const ZONELESS_SCHEDULER_DISABLED = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'scheduler disabled' : ''); // TODO(atscott): Remove in v19. Scheduler should be done with runOutsideAngular. const SCHEDULE_IN_ROOT_ZONE = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'run changes outside zone in root' : ''); /** * Internal implementation of the pending tasks service. */ class PendingTasksInternal { taskId = 0; pendingTasks = new Set(); get _hasPendingTasks() { return this.hasPendingTasks.value; } hasPendingTasks = new BehaviorSubject(false); add() { if (!this._hasPendingTasks) { this.hasPendingTasks.next(true); } const taskId = this.taskId++; this.pendingTasks.add(taskId); return taskId; } has(taskId) { return this.pendingTasks.has(taskId); } remove(taskId) { this.pendingTasks.delete(taskId); if (this.pendingTasks.size === 0 && this._hasPendingTasks) { this.hasPendingTasks.next(false); } } ngOnDestroy() { this.pendingTasks.clear(); if (this._hasPendingTasks) { this.hasPendingTasks.next(false); } } /** @nocollapse */ static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({ token: PendingTasksInternal, providedIn: 'root', factory: () => new PendingTasksInternal(), }); } /** * Service that keeps track of pending tasks contributing to the stableness of Angular * application. While several existing Angular services (ex.: `HttpClient`) will internally manage * tasks influencing stability, this API gives control over stability to library and application * developers for specific cases not covered by Angular internals. * * The concept of stability comes into play in several important scenarios: * - SSR process needs to wait for the application stability before serializing and sending rendered * HTML; * - tests might want to delay assertions until the application becomes stable; * * @usageNotes * ```ts * const pendingTasks = inject(PendingTasks); * const taskCleanup = pendingTasks.add(); * // do work that should block application's stability and then: * taskCleanup(); * ``` * * @publicApi * @developerPreview */ class PendingTasks { internalPendingTasks = inject(PendingTasksInternal); scheduler = inject(ChangeDetectionScheduler); /** * Adds a new task that should block application's stability. * @returns A cleanup function that removes a task when called. */ add() { const taskId = this.internalPendingTasks.add(); return () => { if (!this.internalPendingTasks.has(taskId)) { // This pending task has already been cleared. return; } // Notifying the scheduler will hold application stability open until the next tick. this.scheduler.notify(11 /* NotificationSource.PendingTaskRemoved */); this.internalPendingTasks.remove(taskId); }; } /** * Runs an asynchronous function and blocks the application's stability until the function completes. * * ```ts * pendingTasks.run(async () => { * const userData = await fetch('/api/user'); * this.userData.set(userData); * }); * ``` * * Application stability is at least delayed until the next tick after the `run` method resolves * so it is safe to make additional updates to application state that would require UI synchronization: * * ```ts * const userData = await pendingTasks.run(() => fetch('/api/user')); * this.userData.set(userData); * ``` * * @param fn The asynchronous function to execute */ async run(fn) { const removeTask = this.add(); try { return await fn(); } finally { removeTask(); } } /** @nocollapse */ static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({ token: PendingTasks, providedIn: 'root', factory: () => new PendingTasks(), }); } class EventEmitter_ extends Subject { // tslint:disable-next-line:require-internal-with-underscore __isAsync; destroyRef = undefined; pendingTasks = undefined; constructor(isAsync = false) { super(); this.__isAsync = isAsync; // Attempt to retrieve a `DestroyRef` and `PendingTasks` optionally. // For backwards compatibility reasons, this cannot be required. if (isInInjectionContext()) { // `DestroyRef` is optional because it is not available in all contexts. // But it is useful to properly complete the `EventEmitter` if used with `outputToObservable` // when the component/directive is destroyed. (See `outputToObservable` for more details.) this.destroyRef = inject(DestroyRef, { optional: true }) ?? undefined; this.pendingTasks = inject(PendingTasksInternal, { optional: true }) ?? undefined; } } emit(value) { const prevConsumer = setActiveConsumer$1(null); try { super.next(value); } finally { setActiveConsumer$1(prevConsumer); } } subscribe(observerOrNext, error, complete) { let nextFn = observerOrNext; let errorFn = error || (() => null); let completeFn = complete; if (observerOrNext && typeof observerOrNext === 'object') { const observer = observerOrNext; nextFn = observer.next?.bind(observer); errorFn = observer.error?.bind(observer); completeFn = observer.complete?.bind(observer); } if (this.__isAsync) { errorFn = this.wrapInTimeout(errorFn); if (nextFn) { nextFn = this.wrapInTimeout(nextFn); } if (completeFn) { completeFn = this.wrapInTimeout(completeFn); } } const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn }); if (observerOrNext instanceof Subscription) { observerOrNext.add(sink); } return sink; } wrapInTimeout(fn) { return (value) => { const taskId = this.pendingTasks?.add(); setTimeout(() => { try { fn(value); } finally { if (taskId !== undefined) { this.pendingTasks?.remove(taskId); } } }); }; } } /** * @publicApi */ const EventEmitter = EventEmitter_; function noop(...args) { // Do nothing. } /** * Gets a scheduling function that runs the callback after the first of setTimeout and * requestAnimationFrame resolves. * * - `requestAnimationFrame` ensures that change detection runs ahead of a browser repaint. * This ensures that the create and update passes of a change detection always happen * in the same frame. * - When the browser is resource-starved, `rAF` can execute _before_ a `setTimeout` because * rendering is a very high priority process. This means that `setTimeout` cannot guarantee * same-frame create and update pass, when `setTimeout` is used to schedule the update phase. * - While `rAF` gives us the desirable same-frame updates, it has two limitations that * prevent it from being used alone. First, it does not run in background tabs, which would * prevent Angular from initializing an application when opened in a new tab (for example). * Second, repeated calls to requestAnimationFrame will execute at the refresh rate of the * hardware (~16ms for a 60Hz display). This would cause significant slowdown of tests that * are written with several updates and asserts in the form of "update; await stable; assert;". * - Both `setTimeout` and `rAF` are able to "coalesce" several events from a single user * interaction into a single change detection. Importantly, this reduces view tree traversals when * compared to an alternative timing mechanism like `queueMicrotask`, where change detection would * then be interleaves between each event. * * By running change detection after the first of `setTimeout` and `rAF` to execute, we get the * best of both worlds. * * @returns a function to cancel the scheduled callback */ function scheduleCallbackWithRafRace(callback) { let timeoutId; let animationFrameId; function cleanup() { callback = noop; try { if (animationFrameId !== undefined && typeof cancelAnimationFrame === 'function') { cancelAnimationFrame(animationFrameId); } if (timeoutId !== undefined) { clearTimeout(timeoutId); } } catch { // Clearing/canceling can fail in tests due to the timing of functions being patched and unpatched // Just ignore the errors - we protect ourselves from this issue by also making the callback a no-op. } } timeoutId = setTimeout(() => { callback(); cleanup(); }); if (typeof requestAnimationFrame === 'function') { animationFrameId = requestAnimationFrame(() => { callback(); cleanup(); }); } return () => cleanup(); } function scheduleCallbackWithMicrotask(callback) { queueMicrotask(() => callback()); return () => { callback = noop; }; } class AsyncStackTaggingZoneSpec { createTask; constructor(namePrefix, consoleAsyncStackTaggingImpl = console) { this.name = 'asyncStackTagging for ' + namePrefix; this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null); } // ZoneSpec implementation below. name; onScheduleTask(delegate, _current, target, task) { task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`); return delegate.scheduleTask(target, task); } onInvokeTask(delegate, _currentZone, targetZone, task, applyThis, applyArgs) { let ret; if (task.consoleTask) { ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs)); } else { ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs); } return ret; } } const isAngularZoneProperty = 'isAngularZone'; const angularZoneInstanceIdProperty = isAngularZoneProperty + '_ID'; let ngZoneInstanceId = 0; /** * An injectable service for executing work inside or outside of the Angular zone. * * The most common use of this service is to optimize performance when starting a work consisting of * one or more asynchronous tasks that don't require UI updates or error handling to be handled by * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks * can reenter the Angular zone via {@link #run}. * * * * @usageNotes * ### Example * * ```ts * import {Component, NgZone} from '@angular/core'; * import {NgIf} from '@angular/common'; * * @Component({ * selector: 'ng-zone-demo', * template: ` *

Demo: NgZone

* *

Progress: {{progress}}%

*

Done processing {{label}} of Angular zone!

* * * * `, * }) * export class NgZoneDemo { * progress: number = 0; * label: string; * * constructor(private _ngZone: NgZone) {} * * // Loop inside the Angular zone * // so the UI DOES refresh after each setTimeout cycle * processWithinAngularZone() { * this.label = 'inside'; * this.progress = 0; * this._increaseProgress(() => console.log('Inside Done!')); * } * * // Loop outside of the Angular zone * // so the UI DOES NOT refresh after each setTimeout cycle * processOutsideOfAngularZone() { * this.label = 'outside'; * this.progress = 0; * this._ngZone.runOutsideAngular(() => { * this._increaseProgress(() => { * // reenter the Angular zone and display done * this._ngZone.run(() => { console.log('Outside Done!'); }); * }); * }); * } * * _increaseProgress(doneCallback: () => void) { * this.progress += 1; * console.log(`Current progress: ${this.progress}%`); * * if (this.progress < 100) { * window.setTimeout(() => this._increaseProgress(doneCallback), 10); * } else { * doneCallback(); * } * } * } * ``` * * @publicApi */ class NgZone { hasPendingMacrotasks = false; hasPendingMicrotasks = false; /** * Whether there are no outstanding microtasks or macrotasks. */ isStable = true; /** * Notifies when code enters Angular Zone. This gets fired first on VM Turn. */ onUnstable = new EventEmitter(false); /** * Notifies when there is no more microtasks enqueued in the current VM Turn. * This is a hint for Angular to do change detection, which may enqueue more microtasks. * For this reason this event can fire multiple times per VM Turn. */ onMicrotaskEmpty = new EventEmitter(false); /** * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which * implies we are about to relinquish VM turn. * This event gets called just once. */ onStable = new EventEmitter(false); /** * Notifies that an error has been delivered. */ onError = new EventEmitter(false); constructor(options) { const { enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false, scheduleInRootZone = SCHEDULE_IN_ROOT_ZONE_DEFAULT, } = options; if (typeof Zone == 'undefined') { throw new RuntimeError(908 /* RuntimeErrorCode.MISSING_ZONEJS */, ngDevMode && `In this configuration Angular requires Zone.js`); } Zone.assertZonePatched(); const self = this; self._nesting = 0; self._outer = self._inner = Zone.current; // AsyncStackTaggingZoneSpec provides `linked stack traces` to show // where the async operation is scheduled. For more details, refer // to this article, https://developer.chrome.com/blog/devtools-better-angular-debugging/ // And we only import this AsyncStackTaggingZoneSpec in development mode, // in the production mode, the AsyncStackTaggingZoneSpec will be tree shaken away. if (ngDevMode) { self._inner = self._inner.fork(new AsyncStackTaggingZoneSpec('Angular')); } if (Zone['TaskTrackingZoneSpec']) { self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']()); } if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) { self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']); } // if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be // coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped. self.shouldCoalesceEventChangeDetection = !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection; self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection; self.callbackScheduled = false; self.scheduleInRootZone = scheduleInRootZone; forkInnerZoneWithAngularBehavior(self); } /** This method checks whether the method call happens within an Angular Zone instance. */ static isInAngularZone() { // Zone needs to be checked, because this method might be called even when NoopNgZone is used. return typeof Zone !== 'undefined' && Zone.current.get(isAngularZoneProperty) === true; } /** Assures that the method is called within the Angular Zone, otherwise throws an error. */ static assertInAngularZone() { if (!NgZone.isInAngularZone()) { throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to be in Angular Zone, but it is not!'); } } /** Assures that the method is called outside of the Angular Zone, otherwise throws an error. */ static assertNotInAngularZone() { if (NgZone.isInAngularZone()) { throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to not be in Angular Zone, but it is!'); } } /** * Executes the `fn` function synchronously within the Angular zone and returns value returned by * the function. * * Running functions via `run` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. * * If a synchronous error happens it will be rethrown and not reported via `onError`. */ run(fn, applyThis, applyArgs) { return this._inner.run(fn, applyThis, applyArgs); } /** * Executes the `fn` function synchronously within the Angular zone as a task and returns value * returned by the function. * * Running functions via `runTask` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. * * If a synchronous error happens it will be rethrown and not reported via `onError`. */ runTask(fn, applyThis, applyArgs, name) { const zone = this._inner; const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop); try { return zone.runTask(task, applyThis, applyArgs); } finally { zone.cancelTask(task); } } /** * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not * rethrown. */ runGuarded(fn, applyThis, applyArgs) { return this._inner.runGuarded(fn, applyThis, applyArgs); } /** * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by * the function. * * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do * work that * doesn't trigger Angular change-detection or is subject to Angular's error handling. * * Any future tasks or microtasks scheduled from within this function will continue executing from * outside of the Angular zone. * * Use {@link #run} to reenter the Angular zone and do work that updates the application model. */ runOutsideAngular(fn) { return this._outer.run(fn); } } const EMPTY_PAYLOAD = {}; function checkStable(zone) { // TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent // re-entry. The case is: // // @Component({...}) // export class AppComponent { // constructor(private ngZone: NgZone) { // this.ngZone.onStable.subscribe(() => { // this.ngZone.run(() => console.log('stable');); // }); // } // // The onStable subscriber run another function inside ngZone // which causes `checkStable()` re-entry. // But this fix causes some issues in g3, so this fix will be // launched in another PR. if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { try { zone._nesting++; zone.onMicrotaskEmpty.emit(null); } finally { zone._nesting--; if (!zone.hasPendingMicrotasks) { try { zone.runOutsideAngular(() => zone.onStable.emit(null)); } finally { zone.isStable = true; } } } } } function delayChangeDetectionForEvents(zone) { /** * We also need to check _nesting here * Consider the following case with shouldCoalesceRunChangeDetection = true * * ngZone.run(() => {}); * ngZone.run(() => {}); * * We want the two `ngZone.run()` only trigger one change detection * when shouldCoalesceRunChangeDetection is true. * And because in this case, change detection run in async way(requestAnimationFrame), * so we also need to check the _nesting here to prevent multiple * change detections. */ if (zone.isCheckStableRunning || zone.callbackScheduled) { return; } zone.callbackScheduled = true; function scheduleCheckStable() { scheduleCallbackWithRafRace(() => { zone.callbackScheduled = false; updateMicroTaskStatus(zone); zone.isCheckStableRunning = true; checkStable(zone); zone.isCheckStableRunning = false; }); } if (zone.scheduleInRootZone) { Zone.root.run(() => { scheduleCheckStable(); }); } else { zone._outer.run(() => { scheduleCheckStable(); }); } updateMicroTaskStatus(zone); } function forkInnerZoneWithAngularBehavior(zone) { const delayChangeDetectionForEventsDelegate = () => { delayChangeDetectionForEvents(zone); }; const instanceId = ngZoneInstanceId++; zone._inner = zone._inner.fork({ name: 'angular', properties: { [isAngularZoneProperty]: true, [angularZoneInstanceIdProperty]: instanceId, [angularZoneInstanceIdProperty + instanceId]: true, }, onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => { // Prevent triggering change detection when the flag is detected. if (shouldBeIgnoredByZone(applyArgs)) { return delegate.invokeTask(target, task, applyThis, applyArgs); } try { onEnter(zone); return delegate.invokeTask(target, task, applyThis, applyArgs); } finally { if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') || zone.shouldCoalesceRunChangeDetection) { delayChangeDetectionForEventsDelegate(); } onLeave(zone); } }, onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => { try { onEnter(zone); return delegate.invoke(target, callback, applyThis, applyArgs, source); } finally { if (zone.shouldCoalesceRunChangeDetection && // Do not delay change detection when the task is the scheduler's tick. // We need to synchronously trigger the stability logic so that the // zone-based scheduler can prevent a duplicate ApplicationRef.tick // by first checking if the scheduler tick is running. This does seem a bit roundabout, // but we _do_ still want to trigger all the correct events when we exit the zone.run // (`onMicrotaskEmpty` and `onStable` _should_ emit; developers can have code which // relies on these events happening after change detection runs). // Note: `zone.callbackScheduled` is already in delayChangeDetectionForEventsDelegate // but is added here as well to prevent reads of applyArgs when not necessary !zone.callbackScheduled && !isSchedulerTick(applyArgs)) { delayChangeDetectionForEventsDelegate(); } onLeave(zone); } }, onHasTask: (delegate, current, target, hasTaskState) => { delegate.hasTask(target, hasTaskState); if (current === target) { // We are only interested in hasTask events which originate from our zone // (A child hasTask event is not interesting to us) if (hasTaskState.change == 'microTask') { zone._hasPendingMicrotasks = hasTaskState.microTask; updateMicroTaskStatus(zone); checkStable(zone); } else if (hasTaskState.change == 'macroTask') { zone.hasPendingMacrotasks = hasTaskState.macroTask; } } }, onHandleError: (delegate, current, target, error) => { delegate.handleError(target, error); zone.runOutsideAngular(() => zone.onError.emit(error)); return false; }, }); } function updateMicroTaskStatus(zone) { if (zone._hasPendingMicrotasks || ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) && zone.callbackScheduled === true)) { zone.hasPendingMicrotasks = true; } else { zone.hasPendingMicrotasks = false; } } function onEnter(zone) { zone._nesting++; if (zone.isStable) { zone.isStable = false; zone.onUnstable.emit(null); } } function onLeave(zone) { zone._nesting--; checkStable(zone); } /** * Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls * to framework to perform rendering. */ class NoopNgZone { hasPendingMicrotasks = false; hasPendingMacrotasks = false; isStable = true; onUnstable = new EventEmitter(); onMicrotaskEmpty = new EventEmitter(); onStable = new EventEmitter(); onError = new EventEmitter(); run(fn, applyThis, applyArgs) { return fn.apply(applyThis, applyArgs); } runGuarded(fn, applyThis, applyArgs) { return fn.apply(applyThis, applyArgs); } runOutsideAngular(fn) { return fn(); } runTask(fn, applyThis, applyArgs, name) { return fn.apply(applyThis, applyArgs); } } function shouldBeIgnoredByZone(applyArgs) { return hasApplyArgsData(applyArgs, '__ignore_ng_zone__'); } function isSchedulerTick(applyArgs) { return hasApplyArgsData(applyArgs, '__scheduler_tick__'); } function hasApplyArgsData(applyArgs, key) { if (!Array.isArray(applyArgs)) { return false; } // We should only ever get 1 arg passed through to invokeTask. // Short circuit here incase that behavior changes. if (applyArgs.length !== 1) { return false; } return applyArgs[0]?.data?.[key] === true; } function getNgZone(ngZoneToUse = 'zone.js', options) { if (ngZoneToUse === 'noop') { return new NoopNgZone(); } if (ngZoneToUse === 'zone.js') { return new NgZone(options); } return ngZoneToUse; } /** * Provides a hook for centralized exception handling. * * The default implementation of `ErrorHandler` prints error messages to the `console`. To * intercept error handling, write a custom exception handler that replaces this default as * appropriate for your app. * * @usageNotes * ### Example * * ```ts * class MyErrorHandler implements ErrorHandler { * handleError(error) { * // do something with the exception * } * } * * // Provide in standalone apps * bootstrapApplication(AppComponent, { * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}] * }) * * // Provide in module-based apps * @NgModule({ * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}] * }) * class MyModule {} * ``` * * @publicApi */ class ErrorHandler { /** * @internal */ _console = console; handleError(error) { this._console.error('ERROR', error); } } /** * `InjectionToken` used to configure how to call the `ErrorHandler`. * * `NgZone` is provided by default today so the default (and only) implementation for this * is calling `ErrorHandler.handleError` outside of the Angular zone. */ const INTERNAL_APPLICATION_ERROR_HANDLER = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'internal error handler' : '', { providedIn: 'root', factory: () => { const zone = inject(NgZone); const userErrorHandler = inject(ErrorHandler); return (e) => zone.runOutsideAngular(() => userErrorHandler.handleError(e)); }, }); /** * An `OutputEmitterRef` is created by the `output()` function and can be * used to emit values to consumers of your directive or component. * * Consumers of your directive/component can bind to the output and * subscribe to changes via the bound event syntax. For example: * * ```html * * ``` * * @publicAPI */ class OutputEmitterRef { destroyed = false; listeners = null; errorHandler = inject(ErrorHandler, { optional: true }); /** @internal */ destroyRef = inject(DestroyRef); constructor() { // Clean-up all listeners and mark as destroyed upon destroy. this.destroyRef.onDestroy(() => { this.destroyed = true; this.listeners = null; }); } subscribe(callback) { if (this.destroyed) { throw new RuntimeError(953 /* RuntimeErrorCode.OUTPUT_REF_DESTROYED */, ngDevMode && 'Unexpected subscription to destroyed `OutputRef`. ' + 'The owning directive/component is destroyed.'); } (this.listeners ??= []).push(callback); return { unsubscribe: () => { const idx = this.listeners?.indexOf(callback); if (idx !== undefined && idx !== -1) { this.listeners?.splice(idx, 1); } }, }; } /** Emits a new value to the output. */ emit(value) { if (this.destroyed) { console.warn(formatRuntimeError(953 /* RuntimeErrorCode.OUTPUT_REF_DESTROYED */, ngDevMode && 'Unexpected emit for destroyed `OutputRef`. ' + 'The owning directive/component is destroyed.')); return; } if (this.listeners === null) { return; } const previousConsumer = setActiveConsumer$1(null); try { for (const listenerFn of this.listeners) { try { listenerFn(value); } catch (err) { this.errorHandler?.handleError(err); } } } finally { setActiveConsumer$1(previousConsumer); } } } /** Gets the owning `DestroyRef` for the given output. */ function getOutputDestroyRef(ref) { return ref.destroyRef; } /** * The `output` function allows declaration of Angular outputs in * directives and components. * * You can use outputs to emit values to parent directives and component. * Parents can subscribe to changes via: * * - template event bindings. For example, `(myOutput)="doSomething($event)"` * - programmatic subscription by using `OutputRef#subscribe`. * * @usageNotes * * To use `output()`, import the function from `@angular/core`. * * ```ts * import {output} from '@angular/core'; * ``` * * Inside your component, introduce a new class member and initialize * it with a call to `output`. * * ```ts * @Directive({ * ... * }) * export class MyDir { * nameChange = output(); // OutputEmitterRef * onClick = output(); // OutputEmitterRef * } * ``` * * You can emit values to consumers of your directive, by using * the `emit` method from `OutputEmitterRef`. * * ```ts * updateName(newName: string): void { * this.nameChange.emit(newName); * } * ``` * @initializerApiFunction {"showTypesInSignaturePreview": true} * @publicAPI */ function output(opts) { ngDevMode && assertInInjectionContext(output); return new OutputEmitterRef(); } function inputFunction(initialValue, opts) { ngDevMode && assertInInjectionContext(input); return createInputSignal(initialValue, opts); } function inputRequiredFunction(opts) { ngDevMode && assertInInjectionContext(input); return createInputSignal(REQUIRED_UNSET_VALUE, opts); } /** * The `input` function allows declaration of Angular inputs in directives * and components. * * There are two variants of inputs that can be declared: * * 1. **Optional inputs** with an initial value. * 2. **Required inputs** that consumers need to set. * * By default, the `input` function will declare optional inputs that * always have an initial value. Required inputs can be declared * using the `input.required()` function. * * Inputs are signals. The values of an input are exposed as a `Signal`. * The signal always holds the latest value of the input that is bound * from the parent. * * @usageNotes * To use signal-based inputs, import `input` from `@angular/core`. * * ```ts * import {input} from '@angular/core`; * ``` * * Inside your component, introduce a new class member and initialize * it with a call to `input` or `input.required`. * * ```ts * @Component({ * ... * }) * export class UserProfileComponent { * firstName = input(); // Signal * lastName = input.required(); // Signal * age = input(0) // Signal * } * ``` * * Inside your component template, you can display values of the inputs * by calling the signal. * * ```html * {{firstName()}} * ``` * * @publicAPI * @initializerApiFunction */ const input = (() => { // Note: This may be considered a side-effect, but nothing will depend on // this assignment, unless this `input` constant export is accessed. It's a // self-contained side effect that is local to the user facing`input` export. inputFunction.required = inputRequiredFunction; return inputFunction; })(); /** * Creates an ElementRef from the most recent node. * * @returns The ElementRef instance to use */ function injectElementRef() { return createElementRef(getCurrentTNode(), getLView()); } /** * Creates an ElementRef given a node. * * @param tNode The node for which you'd like an ElementRef * @param lView The view to which the node belongs * @returns The ElementRef instance to use */ function createElementRef(tNode, lView) { return new ElementRef(getNativeByTNode(tNode, lView)); } /** * A wrapper around a native element inside of a View. * * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM * element. * * @security Permitting direct access to the DOM can make your application more vulnerable to * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the * [Security Guide](https://g.co/ng/security). * * @publicApi */ // Note: We don't expose things like `Injector`, `ViewContainer`, ... here, // i.e. users have to ask for what they need. With that, we can build better analysis tools // and could do better codegen in the future. class ElementRef { /** *
*
Use with caution
*

* Use this API as the last resort when direct access to DOM is needed. Use templating and * data-binding provided by Angular instead. If used, it is recommended in combination with * {@link /best-practices/security#direct-use-of-the-dom-apis-and-explicit-sanitization-calls DomSanitizer} * for maxiumum security; *

*
*/ nativeElement; constructor(nativeElement) { this.nativeElement = nativeElement; } /** * @internal * @nocollapse */ static __NG_ELEMENT_ID__ = injectElementRef; } /** * Unwraps `ElementRef` and return the `nativeElement`. * * @param value value to unwrap * @returns `nativeElement` if `ElementRef` otherwise returns value as is. */ function unwrapElementRef(value) { return value instanceof ElementRef ? value.nativeElement : value; } /** * Checks if the given `value` is a reactive `Signal`. */ function isSignal(value) { return typeof value === 'function' && value[SIGNAL] !== undefined; } /** * Utility function used during template type checking to extract the value from a `WritableSignal`. * @codeGenApi */ function ɵunwrapWritableSignal(value) { // Note: the function uses `WRITABLE_SIGNAL` as a brand instead of `WritableSignal`, // because the latter incorrectly unwraps non-signal getter functions. return null; } /** * Create a `Signal` that can be set or updated directly. */ function signal(initialValue, options) { const signalFn = createSignal(initialValue, options?.equal); const node = signalFn[SIGNAL]; signalFn.set = (newValue) => signalSetFn(node, newValue); signalFn.update = (updateFn) => signalUpdateFn(node, updateFn); signalFn.asReadonly = signalAsReadonlyFn.bind(signalFn); if (ngDevMode) { signalFn.toString = () => `[Signal: ${signalFn()}]`; node.debugName = options?.debugName; } return signalFn; } function signalAsReadonlyFn() { const node = this[SIGNAL]; if (node.readonlyFn === undefined) { const readonlyFn = () => this(); readonlyFn[SIGNAL] = node; node.readonlyFn = readonlyFn; } return node.readonlyFn; } /** * Checks if the given `value` is a writeable signal. */ function isWritableSignal(value) { return isSignal(value) && typeof value.set === 'function'; } function symbolIterator() { // @ts-expect-error accessing a private member return this._results[Symbol.iterator](); } /** * An unmodifiable list of items that Angular keeps up to date when the state * of the application changes. * * The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList} * provide. * * Implements an iterable interface, therefore it can be used in both ES6 * javascript `for (var i of items)` loops as well as in Angular templates with * `*ngFor="let i of myList"`. * * Changes can be observed by subscribing to the changes `Observable`. * * NOTE: In the future this class will implement an `Observable` interface. * * @usageNotes * ### Example * ```ts * @Component({...}) * class Container { * @ViewChildren(Item) items:QueryList; * } * ``` * * @publicApi */ class QueryList { _emitDistinctChangesOnly; dirty = true; _onDirty = undefined; _results = []; _changesDetected = false; _changes = undefined; length = 0; first = undefined; last = undefined; /** * Returns `Observable` of `QueryList` notifying the subscriber of changes. */ get changes() { return (this._changes ??= new Subject()); } /** * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in * the same result) */ constructor(_emitDistinctChangesOnly = false) { this._emitDistinctChangesOnly = _emitDistinctChangesOnly; } /** * Returns the QueryList entry at `index`. */ get(index) { return this._results[index]; } /** * See * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) */ map(fn) { return this._results.map(fn); } filter(fn) { return this._results.filter(fn); } /** * See * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ find(fn) { return this._results.find(fn); } /** * See * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ reduce(fn, init) { return this._results.reduce(fn, init); } /** * See * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) */ forEach(fn) { this._results.forEach(fn); } /** * See * [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) */ some(fn) { return this._results.some(fn); } /** * Returns a copy of the internal results list as an Array. */ toArray() { return this._results.slice(); } toString() { return this._results.toString(); } /** * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that * on change detection, it will not notify of changes to the queries, unless a new change * occurs. * * @param resultsTree The query results to store * @param identityAccessor Optional function for extracting stable object identity from a value * in the array. This function is executed for each element of the query result list while * comparing current query list with the new one (provided as a first argument of the `reset` * function) to detect if the lists are different. If the function is not provided, elements * are compared as is (without any pre-processing). */ reset(resultsTree, identityAccessor) { this.dirty = false; const newResultFlat = flatten(resultsTree); if ((this._changesDetected = !arrayEquals(this._results, newResultFlat, identityAccessor))) { this._results = newResultFlat; this.length = newResultFlat.length; this.last = newResultFlat[this.length - 1]; this.first = newResultFlat[0]; } } /** * Triggers a change event by emitting on the `changes` {@link EventEmitter}. */ notifyOnChanges() { if (this._changes !== undefined && (this._changesDetected || !this._emitDistinctChangesOnly)) this._changes.next(this); } /** @internal */ onDirty(cb) { this._onDirty = cb; } /** internal */ setDirty() { this.dirty = true; this._onDirty?.(); } /** internal */ destroy() { if (this._changes !== undefined) { this._changes.complete(); this._changes.unsubscribe(); } } [Symbol.iterator] = (() => symbolIterator)(); } /** * The name of an attribute that can be added to the hydration boundary node * (component host node) to disable hydration for the content within that boundary. */ const SKIP_HYDRATION_ATTR_NAME = 'ngSkipHydration'; /** Lowercase name of the `ngSkipHydration` attribute used for case-insensitive comparisons. */ const SKIP_HYDRATION_ATTR_NAME_LOWER_CASE = 'ngskiphydration'; /** * Helper function to check if a given TNode has the 'ngSkipHydration' attribute. */ function hasSkipHydrationAttrOnTNode(tNode) { const attrs = tNode.mergedAttrs; if (attrs === null) return false; // only ever look at the attribute name and skip the values for (let i = 0; i < attrs.length; i += 2) { const value = attrs[i]; // This is a marker, which means that the static attributes section is over, // so we can exit early. if (typeof value === 'number') return false; if (typeof value === 'string' && value.toLowerCase() === SKIP_HYDRATION_ATTR_NAME_LOWER_CASE) { return true; } } return false; } /** * Helper function to check if a given RElement has the 'ngSkipHydration' attribute. */ function hasSkipHydrationAttrOnRElement(rNode) { return rNode.hasAttribute(SKIP_HYDRATION_ATTR_NAME); } /** * Checks whether a TNode has a flag to indicate that it's a part of * a skip hydration block. */ function hasInSkipHydrationBlockFlag(tNode) { return (tNode.flags & 128 /* TNodeFlags.inSkipHydrationBlock */) === 128 /* TNodeFlags.inSkipHydrationBlock */; } /** * Helper function that determines if a given node is within a skip hydration block * by navigating up the TNode tree to see if any parent nodes have skip hydration * attribute. */ function isInSkipHydrationBlock(tNode) { if (hasInSkipHydrationBlockFlag(tNode)) { return true; } let currentTNode = tNode.parent; while (currentTNode) { if (hasInSkipHydrationBlockFlag(tNode) || hasSkipHydrationAttrOnTNode(currentTNode)) { return true; } currentTNode = currentTNode.parent; } return false; } /** * Check if an i18n block is in a skip hydration section by looking at a parent TNode * to determine if this TNode is in a skip hydration section or the TNode has * the `ngSkipHydration` attribute. */ function isI18nInSkipHydrationBlock(parentTNode) { return (hasInSkipHydrationBlockFlag(parentTNode) || hasSkipHydrationAttrOnTNode(parentTNode) || isInSkipHydrationBlock(parentTNode)); } /** * The strategy that the default change detector uses to detect changes. * When set, takes effect the next time change detection is triggered. * * @see {@link /api/core/ChangeDetectorRef?tab=usage-notes Change detection usage} * @see {@link /best-practices/skipping-subtrees Skipping component subtrees} * * @publicApi */ var ChangeDetectionStrategy; (function (ChangeDetectionStrategy) { /** * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated * until reactivated by setting the strategy to `Default` (`CheckAlways`). * Change detection can still be explicitly invoked. * This strategy applies to all child directives and cannot be overridden. */ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush"; /** * Use the default `CheckAlways` strategy, in which change detection is automatic until * explicitly deactivated. */ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default"; })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {})); // Keeps track of the currently-active LViews. const TRACKED_LVIEWS = new Map(); // Used for generating unique IDs for LViews. let uniqueIdCounter = 0; /** Gets a unique ID that can be assigned to an LView. */ function getUniqueLViewId() { return uniqueIdCounter++; } /** Starts tracking an LView. */ function registerLView(lView) { ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered'); TRACKED_LVIEWS.set(lView[ID], lView); } /** Gets an LView by its unique ID. */ function getLViewById(id) { ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number'); return TRACKED_LVIEWS.get(id) || null; } /** Stops tracking an LView. */ function unregisterLView(lView) { ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID'); TRACKED_LVIEWS.delete(lView[ID]); } /** Gets the currently-tracked views. */ function getTrackedLViews() { return TRACKED_LVIEWS; } /** * The internal view context which is specific to a given DOM element, directive or * component instance. Each value in here (besides the LView and element node details) * can be present, null or undefined. If undefined then it implies the value has not been * looked up yet, otherwise, if null, then a lookup was executed and nothing was found. * * Each value will get filled when the respective value is examined within the getContext * function. The component, element and each directive instance will share the same instance * of the context. */ class LContext { lViewId; nodeIndex; native; /** * The instance of the Component node. */ component; /** * The list of active directives that exist on this element. */ directives; /** * The map of local references (local reference name => element or directive instance) that * exist on this element. */ localRefs; /** Component's parent view data. */ get lView() { return getLViewById(this.lViewId); } constructor( /** * ID of the component's parent view data. */ lViewId, /** * The index instance of the node. */ nodeIndex, /** * The instance of the DOM node that is attached to the lNode. */ native) { this.lViewId = lViewId; this.nodeIndex = nodeIndex; this.native = native; } } /** * Returns the matching `LContext` data for a given DOM node, directive or component instance. * * This function will examine the provided DOM element, component, or directive instance\'s * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched * value will be that of the newly created `LContext`. * * If the monkey-patched value is the `LView` instance then the context value for that * target will be created and the monkey-patch reference will be updated. Therefore when this * function is called it may mutate the provided element\'s, component\'s or any of the associated * directive\'s monkey-patch values. * * If the monkey-patch value is not detected then the code will walk up the DOM until an element * is found which contains a monkey-patch reference. When that occurs then the provided element * will be updated with a new context (which is then returned). If the monkey-patch value is not * detected for a component/directive instance then it will throw an error (all components and * directives should be automatically monkey-patched by ivy). * * @param target Component, Directive or DOM Node. */ function getLContext(target) { let mpValue = readPatchedData(target); if (mpValue) { // only when it's an array is it considered an LView instance // ... otherwise it's an already constructed LContext instance if (isLView(mpValue)) { const lView = mpValue; let nodeIndex; let component = undefined; let directives = undefined; if (isComponentInstance(target)) { nodeIndex = findViaComponent(lView, target); if (nodeIndex == -1) { throw new Error('The provided component was not found in the application'); } component = target; } else if (isDirectiveInstance(target)) { nodeIndex = findViaDirective(lView, target); if (nodeIndex == -1) { throw new Error('The provided directive was not found in the application'); } directives = getDirectivesAtNodeIndex(nodeIndex, lView); } else { nodeIndex = findViaNativeElement(lView, target); if (nodeIndex == -1) { return null; } } // the goal is not to fill the entire context full of data because the lookups // are expensive. Instead, only the target data (the element, component, container, ICU // expression or directive details) are filled into the context. If called multiple times // with different target values then the missing target data will be filled in. const native = unwrapRNode(lView[nodeIndex]); const existingCtx = readPatchedData(native); const context = existingCtx && !Array.isArray(existingCtx) ? existingCtx : createLContext(lView, nodeIndex, native); // only when the component has been discovered then update the monkey-patch if (component && context.component === undefined) { context.component = component; attachPatchData(context.component, context); } // only when the directives have been discovered then update the monkey-patch if (directives && context.directives === undefined) { context.directives = directives; for (let i = 0; i < directives.length; i++) { attachPatchData(directives[i], context); } } attachPatchData(context.native, context); mpValue = context; } } else { const rElement = target; ngDevMode && assertDomNode(rElement); // if the context is not found then we need to traverse upwards up the DOM // to find the nearest element that has already been monkey patched with data let parent = rElement; while ((parent = parent.parentNode)) { const parentContext = readPatchedData(parent); if (parentContext) { const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView; // the edge of the app was also reached here through another means // (maybe because the DOM was changed manually). if (!lView) { return null; } const index = findViaNativeElement(lView, rElement); if (index >= 0) { const native = unwrapRNode(lView[index]); const context = createLContext(lView, index, native); attachPatchData(native, context); mpValue = context; break; } } } } return mpValue || null; } /** * Creates an empty instance of a `LContext` context */ function createLContext(lView, nodeIndex, native) { return new LContext(lView[ID], nodeIndex, native); } /** * Takes a component instance and returns the view for that component. * * @param componentInstance * @returns The component's view */ function getComponentViewByInstance(componentInstance) { let patchedData = readPatchedData(componentInstance); let lView; if (isLView(patchedData)) { const contextLView = patchedData; const nodeIndex = findViaComponent(contextLView, componentInstance); lView = getComponentLViewByIndex(nodeIndex, contextLView); const context = createLContext(contextLView, nodeIndex, lView[HOST]); context.component = componentInstance; attachPatchData(componentInstance, context); attachPatchData(context.native, context); } else { const context = patchedData; const contextLView = context.lView; ngDevMode && assertLView(contextLView); lView = getComponentLViewByIndex(context.nodeIndex, contextLView); } return lView; } /** * This property will be monkey-patched on elements, components and directives. */ const MONKEY_PATCH_KEY_NAME = '__ngContext__'; /** * Assigns the given data to the given target (which could be a component, * directive or DOM node instance) using monkey-patching. */ function attachPatchData(target, data) { ngDevMode && assertDefined(target, 'Target expected'); // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this // for `LView`, because we have control over when an `LView` is created and destroyed, whereas // we can't know when to remove an `LContext`. if (isLView(data)) { target[MONKEY_PATCH_KEY_NAME] = data[ID]; registerLView(data); } else { target[MONKEY_PATCH_KEY_NAME] = data; } } /** * Returns the monkey-patch value data present on the target (which could be * a component, directive or a DOM node). */ function readPatchedData(target) { ngDevMode && assertDefined(target, 'Target expected'); const data = target[MONKEY_PATCH_KEY_NAME]; return typeof data === 'number' ? getLViewById(data) : data || null; } function readPatchedLView(target) { const value = readPatchedData(target); if (value) { return (isLView(value) ? value : value.lView); } return null; } function isComponentInstance(instance) { return instance && instance.constructor && instance.constructor.ɵcmp; } function isDirectiveInstance(instance) { return instance && instance.constructor && instance.constructor.ɵdir; } /** * Locates the element within the given LView and returns the matching index */ function findViaNativeElement(lView, target) { const tView = lView[TVIEW]; for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) { if (unwrapRNode(lView[i]) === target) { return i; } } return -1; } /** * Locates the next tNode (child, sibling or parent). */ function traverseNextElement(tNode) { if (tNode.child) { return tNode.child; } else if (tNode.next) { return tNode.next; } else { // Let's take the following template:
text
// After checking the text node, we need to find the next parent that has a "next" TNode, // in this case the parent `div`, so that we can find the component. while (tNode.parent && !tNode.parent.next) { tNode = tNode.parent; } return tNode.parent && tNode.parent.next; } } /** * Locates the component within the given LView and returns the matching index */ function findViaComponent(lView, componentInstance) { const componentIndices = lView[TVIEW].components; if (componentIndices) { for (let i = 0; i < componentIndices.length; i++) { const elementComponentIndex = componentIndices[i]; const componentView = getComponentLViewByIndex(elementComponentIndex, lView); if (componentView[CONTEXT] === componentInstance) { return elementComponentIndex; } } } else { const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView); const rootComponent = rootComponentView[CONTEXT]; if (rootComponent === componentInstance) { // we are dealing with the root element here therefore we know that the // element is the very first element after the HEADER data in the lView return HEADER_OFFSET; } } return -1; } /** * Locates the directive within the given LView and returns the matching index */ function findViaDirective(lView, directiveInstance) { // if a directive is monkey patched then it will (by default) // have a reference to the LView of the current view. The // element bound to the directive being search lives somewhere // in the view data. We loop through the nodes and check their // list of directives for the instance. let tNode = lView[TVIEW].firstChild; while (tNode) { const directiveIndexStart = tNode.directiveStart; const directiveIndexEnd = tNode.directiveEnd; for (let i = directiveIndexStart; i < directiveIndexEnd; i++) { if (lView[i] === directiveInstance) { return tNode.index; } } tNode = traverseNextElement(tNode); } return -1; } /** * Returns a list of directives applied to a node at a specific index. The list includes * directives matched by selector and any host directives, but it excludes components. * Use `getComponentAtNodeIndex` to find the component applied to a node. * * @param nodeIndex The node index * @param lView The target view data */ function getDirectivesAtNodeIndex(nodeIndex, lView) { const tNode = lView[TVIEW].data[nodeIndex]; if (tNode.directiveStart === 0) return EMPTY_ARRAY; const results = []; for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) { const directiveInstance = lView[i]; if (!isComponentInstance(directiveInstance)) { results.push(directiveInstance); } } return results; } function getComponentAtNodeIndex(nodeIndex, lView) { const tNode = lView[TVIEW].data[nodeIndex]; return isComponentHost(tNode) ? lView[tNode.directiveStart + tNode.componentOffset] : null; } /** * Returns a map of local references (local reference name => element or directive instance) that * exist on a given element. */ function discoverLocalRefs(lView, nodeIndex) { const tNode = lView[TVIEW].data[nodeIndex]; if (tNode && tNode.localNames) { const result = {}; let localIndex = tNode.index + 1; for (let i = 0; i < tNode.localNames.length; i += 2) { result[tNode.localNames[i]] = lView[localIndex]; localIndex++; } return result; } return null; } /** * Retrieve the root view from any component or `LView` by walking the parent `LView` until * reaching the root `LView`. * * @param componentOrLView any component or `LView` */ function getRootView(componentOrLView) { ngDevMode && assertDefined(componentOrLView, 'component'); let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView); while (lView && !isRootView(lView)) { lView = getLViewParent(lView); } ngDevMode && assertLView(lView); return lView; } /** * Returns the context information associated with the application where the target is situated. It * does this by walking the parent views until it gets to the root view, then getting the context * off of that. * * @param viewOrComponent the `LView` or component to get the root context for. */ function getRootContext(viewOrComponent) { const rootView = getRootView(viewOrComponent); ngDevMode && assertDefined(rootView[CONTEXT], 'Root view has no context. Perhaps it is disconnected?'); return rootView[CONTEXT]; } /** * Gets the first `LContainer` in the LView or `null` if none exists. */ function getFirstLContainer(lView) { return getNearestLContainer(lView[CHILD_HEAD]); } /** * Gets the next `LContainer` that is a sibling of the given container. */ function getNextLContainer(container) { return getNearestLContainer(container[NEXT]); } function getNearestLContainer(viewOrContainer) { while (viewOrContainer !== null && !isLContainer(viewOrContainer)) { viewOrContainer = viewOrContainer[NEXT]; } return viewOrContainer; } /** * Retrieves the component instance associated with a given DOM element. * * @usageNotes * Given the following DOM structure: * * ```html * *
* *
*
* ``` * * Calling `getComponent` on `` will return the instance of `ChildComponent` * associated with this DOM element. * * Calling the function on `` will return the `MyApp` instance. * * * @param element DOM element from which the component should be retrieved. * @returns Component instance associated with the element or `null` if there * is no component associated with it. * * @publicApi */ function getComponent(element) { ngDevMode && assertDomElement(element); const context = getLContext(element); if (context === null) return null; if (context.component === undefined) { const lView = context.lView; if (lView === null) { return null; } context.component = getComponentAtNodeIndex(context.nodeIndex, lView); } return context.component; } /** * If inside an embedded view (e.g. `*ngIf` or `*ngFor`), retrieves the context of the embedded * view that the element is part of. Otherwise retrieves the instance of the component whose view * owns the element (in this case, the result is the same as calling `getOwningComponent`). * * @param element Element for which to get the surrounding component instance. * @returns Instance of the component that is around the element or null if the element isn't * inside any component. * * @publicApi */ function getContext(element) { assertDomElement(element); const context = getLContext(element); const lView = context ? context.lView : null; return lView === null ? null : lView[CONTEXT]; } /** * Retrieves the component instance whose view contains the DOM element. * * For example, if `` is used in the template of `` * (i.e. a `ViewChild` of ``), calling `getOwningComponent` on `` * would return ``. * * @param elementOrDir DOM element, component or directive instance * for which to retrieve the root components. * @returns Component instance whose view owns the DOM element or null if the element is not * part of a component view. * * @publicApi */ function getOwningComponent(elementOrDir) { const context = getLContext(elementOrDir); let lView = context ? context.lView : null; if (lView === null) return null; let parent; while (lView[TVIEW].type === 2 /* TViewType.Embedded */ && (parent = getLViewParent(lView))) { lView = parent; } return isRootView(lView) ? null : lView[CONTEXT]; } /** * Retrieves all root components associated with a DOM element, directive or component instance. * Root components are those which have been bootstrapped by Angular. * * @param elementOrDir DOM element, component or directive instance * for which to retrieve the root components. * @returns Root components associated with the target object. * * @publicApi */ function getRootComponents(elementOrDir) { const lView = readPatchedLView(elementOrDir); return lView !== null ? [getRootContext(lView)] : []; } /** * Retrieves an `Injector` associated with an element, component or directive instance. * * @param elementOrDir DOM element, component or directive instance for which to * retrieve the injector. * @returns Injector associated with the element, component or directive instance. * * @publicApi */ function getInjector(elementOrDir) { const context = getLContext(elementOrDir); const lView = context ? context.lView : null; if (lView === null) return Injector.NULL; const tNode = lView[TVIEW].data[context.nodeIndex]; return new NodeInjector(tNode, lView); } /** * Retrieve a set of injection tokens at a given DOM node. * * @param element Element for which the injection tokens should be retrieved. */ function getInjectionTokens(element) { const context = getLContext(element); const lView = context ? context.lView : null; if (lView === null) return []; const tView = lView[TVIEW]; const tNode = tView.data[context.nodeIndex]; const providerTokens = []; const startIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */; const endIndex = tNode.directiveEnd; for (let i = startIndex; i < endIndex; i++) { let value = tView.data[i]; if (isDirectiveDefHack(value)) { // The fact that we sometimes store Type and sometimes DirectiveDef in this location is a // design flaw. We should always store same type so that we can be monomorphic. The issue // is that for Components/Directives we store the def instead the type. The correct behavior // is that we should always be storing injectable type in this location. value = value.type; } providerTokens.push(value); } return providerTokens; } /** * Retrieves directive instances associated with a given DOM node. Does not include * component instances. * * @usageNotes * Given the following DOM structure: * * ```html * * * * * ``` * * Calling `getDirectives` on `