/** * @license Angular v20.0.0 * (c) 2010-2025 Google LLC. https://angular.io/ * License: MIT */ import { attachInjectFlag, _global, resolveForwardRef, ɵɵinvalidFactoryDep as __invalidFactoryDep, ɵɵinject as __inject, ɵɵdefineInjector as __defineInjector, ɵɵdefineInjectable as __defineInjectable, newArray, assertString, EMPTY_OBJ, assertFirstCreatePass, assertDefined, assertNotEqual, FLAGS, assertEqual, isInCheckNoChangesMode, PREORDER_HOOK_FLAGS, throwError, assertNumber, assertGreaterThan, HEADER_OFFSET, DECLARATION_VIEW, throwCyclicDependencyError, stringifyForError, setInjectorProfilerContext, setInjectImplementation, enterDI, emitInjectorToCreateInstanceEvent, emitInstanceCreatedByInjectorEvent, assertDirectiveDef, leaveDI, isRootView, assertTNodeForLView, runInInjectorProfilerContext, getCurrentTNode, getLView, throwProviderNotFoundError, assertNodeInjector, injectRootLimpMode, assertIndexInRange, TVIEW, EMBEDDED_VIEW_INJECTOR, T_HOST, NG_ELEMENT_ID, INJECTOR$1 as INJECTOR, isComponentDef, isComponentHost, convertToBitFlags, NG_FACTORY_DEF, isForwardRef, getFactoryDef, DECLARATION_COMPONENT_VIEW, RuntimeError, NG_PROV_DEF, getClosureSafeProperty, getNativeByTNode, flatten, arrayEquals, ID, isLView, assertDomNode, getComponentLViewByIndex, CONTEXT, EMPTY_ARRAY, unwrapRNode, assertLView, HOST, getLViewParent, CHILD_HEAD, NEXT, isLContainer, Injector, CLEANUP, getComponentDef, getDirectiveDef, InjectionToken, inject, isContentQueryHost, setCurrentQueryIndex, XSS_SECURITY_URL, renderStringify, ENVIRONMENT, makeEnvironmentProviders, formatRuntimeError, resetPreOrderHookFlags, PARENT, RENDERER, HYDRATION, CHILD_TAIL, assertSame, assertFirstUpdatePass, getSelectedIndex, getTView, assertIndexInDeclRange, setSelectedIndex, INTERNAL_APPLICATION_ERROR_HANDLER, assertNotSame, setCurrentDirectiveIndex, stringify, getCurrentDirectiveIndex, unwrapLView, assertNotReactive, enterView, QUERIES, leaveView, isCreationMode, assertLContainer, DECLARATION_LCONTAINER, isDestroyed, MOVED_VIEWS, NATIVE, assertProjectionSlots, CONTAINER_HEADER_OFFSET, assertParentView, REACTIVE_TEMPLATE_CONSUMER, ON_DESTROY_HOOKS, assertFunction, EFFECTS, AFTER_RENDER_SEQUENCES_TO_ADD, markViewForRefresh, markAncestorsForTraversal, setIsInCheckNoChangesMode, setIsRefreshingViews, isExhaustiveCheckNoChanges, requiresRefreshOrTraversal, setBindingIndex, EFFECTS_TO_SCHEDULE, setBindingRootForHostBindings, viewAttachedToChangeDetector, isRefreshingViews, CheckNoChangesMode, removeFromArray, addToArray, updateAncestorTraversalFlagsOnAttach, storeLViewOnDestroy, VIEW_REFS, assertGreaterThanOrEqual, isInI18nBlock, getCurrentParentTNode, assertTNodeForTView, setCurrentTNode, getCurrentTNodePlaceholderOk, isCurrentTNodeParent, isInSkipHydrationBlock as isInSkipHydrationBlock$1, assertTNode, assertTIcu, assertNumberInRange, DEHYDRATED_VIEWS, getNgModuleDef, getPipeDef as getPipeDef$1, getNgModuleDefOrThrow, isStandalone, concatStringsWithSpace, assertInjectImplementationNotEqual, emitInjectEvent, getConstant, assertLessThan, getOrCreateTViewCleanup, getOrCreateLViewCleanup, isDirectiveHost, assertNotDefined, getSelectedTNode, nextBindingIndex, getTNode, debugStringifyTypeForError, getDirectiveDefOrThrow, ChangeDetectionScheduler, assertComponentType, EnvironmentInjector, SVG_NAMESPACE, MATH_ML_NAMESPACE, viewAttachedToContainer, storeCleanupWithContext, signal, createInjectorWithoutInjectorInstances, R3Injector, getNullInjector, internalImportProvidersFrom, initNgDevMode, fillProperties, wasLastNodeCreated, getBindingsEnabled, lastNodeWasCreated, isInInjectionContext, DestroyRef, PendingTasksInternal, noop, ErrorHandler, assertNotInReactiveContext, assertInInjectionContext, ViewContext, removeLViewOnDestroy, walkUpViews, getNativeByIndex, assertElement, arrayInsert2, arraySplice, setInjectorProfiler, NullInjector, ENVIRONMENT_INITIALIZER, INJECTOR_DEF_TYPES, walkProviderTree, getInjectorDef, deepForEach, isTypeProvider, isSignal, runInInjectionContext, ZONELESS_ENABLED, EffectScheduler, PendingTasks, assertHasParent, getElementDepthCount, getNamespace, enterSkipHydrationBlock, getBindingIndex, increaseElementDepthCount, setCurrentTNodeAsNotParent, isSkipHydrationRootTNode, leaveSkipHydrationBlock, decreaseElementDepthCount, getCurrentDirectiveDef, assertIndexInExpandoRange, assertOneOf, setInI18nBlock, nextContextImpl, getCurrentQueryIndex, getContextLView, load, keyValueArrayIndexOf, incrementBindingIndex, keyValueArrayGet, keyValueArraySet, isWritableSignal, store, providerToFactory, emitProviderConfiguredEvent, isClassProvider, getBindingRoot, NG_COMP_DEF, forwardRef, ɵɵrestoreView as __restoreView, ɵɵdisableBindings as __disableBindings, ɵɵenableBindings as __enableBindings, ɵɵnamespaceSVG as __namespaceSVG, ɵɵnamespaceMathML as __namespaceMathML, ɵɵnamespaceHTML as __namespaceHTML, ɵɵresetView as __resetView, NG_MOD_DEF, NG_INJ_DEF, NG_DIR_DEF, NG_PIPE_DEF, ZONELESS_SCHEDULER_DISABLED, SCHEDULE_IN_ROOT_ZONE, PROVIDED_ZONELESS, getNativeByTNodeOrNull } from './root_effect_scheduler-0BxwqIgm.mjs'; import { setActiveConsumer, SIGNAL, consumerDestroy, REACTIVE_NODE, consumerPollProducersForChange, consumerBeforeComputation, getActiveConsumer, consumerAfterComputation, createComputed, setThrowInvalidWriteToSignalError } from './signal-ePSl6jXn.mjs'; import { Subject, Subscription } from 'rxjs'; import { setActiveConsumer as setActiveConsumer$1 } from '@angular/core/primitives/signals'; import { map } from 'rxjs/operators'; import { Attribute as Attribute$1 } from './attribute-BWp59EjE.mjs'; /** * 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; }); } /** * 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 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; } /** * 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); } const profilerCallbacks = []; const NOOP_PROFILER_REMOVAL = () => { }; function removeProfiler(profiler) { const profilerIdx = profilerCallbacks.indexOf(profiler); if (profilerIdx !== -1) { profilerCallbacks.splice(profilerIdx, 1); } } /** * Adds a callback function which will be invoked before and after performing certain actions at * runtime (for example, before and after running change detection). Multiple profiler callbacks can be set: * in this case profiling events are reported to every registered callback. * * 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 all profilers. * @returns a cleanup function that, when invoked, removes a given profiler callback. */ function setProfiler(profiler) { if (profiler !== null) { if (!profilerCallbacks.includes(profiler)) { profilerCallbacks.push(profiler); } return () => removeProfiler(profiler); } else { profilerCallbacks.length = 0; return NOOP_PROFILER_REMOVAL; } } /** * Profiler function which wraps user code executed by the runtime. * * @param event ProfilerEvent corresponding to the execution context * @param instance component instance * @param eventFn function associated with event. * For example a template function, lifecycle hook, or output listener. * The value depends on the execution context */ const profiler = function (event, instance = null, eventFn) { for (let i = 0; i < profilerCallbacks.length; i++) { const profilerCallback = profilerCallbacks[i]; profilerCallback(event, instance, eventFn); } }; /** * 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(null); try { hook.call(directive); } finally { setActiveConsumer(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++]; renderer.setAttribute(native, attrName, attrVal, namespaceURI); } else { // attrName is string; const attrName = value; const attrVal = attrs[++i]; // Standard attributes 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 & 8 /* InternalInjectFlags.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 `InternalInjectFlags.Optional` * @returns the value from the injector or throws an exception */ function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) { if (flags & 8 /* InternalInjectFlags.Optional */ && notFoundValue === undefined) { // This must be set or the NullInjector will throw for optional deps notFoundValue = null; } if ((flags & (2 /* InternalInjectFlags.Self */ | 1 /* InternalInjectFlags.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 & 8 /* InternalInjectFlags.Optional */); } else { return injectRootLimpMode(token, notFoundValue, flags & 8 /* InternalInjectFlags.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 `InternalInjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function getOrCreateInjectable(tNode, lView, token, flags = 0 /* InternalInjectFlags.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 & 2 /* InternalInjectFlags.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 `InternalInjectFlags.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 & 1 /* InternalInjectFlags.Host */ ? notFoundValueOrThrow(notFoundValue, token, flags) : lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue); } try { let value; if (ngDevMode) { runInInjectorProfilerContext(new NodeInjector(getCurrentTNode(), getLView()), token, () => { emitInjectorToCreateInstanceEvent(token); value = bloomHash(flags); emitInstanceCreatedByInjectorEvent(value); }); } else { value = bloomHash(flags); } if (value == null && !(flags & 8 /* InternalInjectFlags.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 & 1 /* InternalInjectFlags.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 & 4 /* InternalInjectFlags.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 & 1 /* InternalInjectFlags.Host */ && hostTElementNode === tNode; const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase); if (injectableIdx !== null) { return getNodeInjectable(lView, currentTView, injectableIdx, tNode); } 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) { 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; // tData indexes mirror the concrete instances in its corresponding LView. // lView[index] here is either the injectable instance 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]; let prevInjectContext; if (ngDevMode) { const injector = new NodeInjector(tNode, lView); prevInjectContext = setInjectorProfilerContext({ injector, token }); } const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null; const success = enterDI(lView, tNode, 0 /* InternalInjectFlags.Default */); ngDevMode && assertEqual(success, true, "Because flags do not contain `SkipSelf' we expect this to always succeed."); try { ngDevMode && emitInjectorToCreateInstanceEvent(token); value = lView[index] = factory.factory(undefined, 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 & 2 /* InternalInjectFlags.Self */) && !(flags & 1 /* InternalInjectFlags.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 `InternalInjectFlags.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 | 2 /* InternalInjectFlags.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)); /** * 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; } 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 `