@lithiumjs/angular
Version:
Reactive components made easy. Lithium provides utilities that enable seamless reactive state and event interactions for Angular components.
1,045 lines (1,033 loc) • 80.6 kB
JavaScript
import { Subject, Subscription, EMPTY, Observable, BehaviorSubject, ReplaySubject, from, throwError, merge, combineLatest, forkJoin, of } from 'rxjs';
import { EventEmitter, resolveForwardRef, Injector, InjectionToken } from '@angular/core';
import { mergeMap, skip, map, tap, distinctUntilChanged, filter, switchMap, takeUntil, flatMap, take } from 'rxjs/operators';
var Metadata;
(function (Metadata) {
const LI_METADATA_ROOT = Symbol('$LI_');
function requireMetadata(symbol, target, defaultValue) {
if (!hasMetadata(symbol, target)) {
setMetadata(symbol, target, defaultValue);
}
return getOwnMetadata(symbol, target) || getMetadata(symbol, target);
}
Metadata.requireMetadata = requireMetadata;
function requireOwnMetadata(symbol, target, defaultValue) {
if (!hasOwnMetadata(symbol, target)) {
setMetadata(symbol, target, defaultValue);
}
return getOwnMetadata(symbol, target);
}
Metadata.requireOwnMetadata = requireOwnMetadata;
function getMetadataMap(target) {
return (getMetadataKeys(target) || [])
.reduce((map, key) => map.set(key, getMetadata(key, target)), new Map());
}
Metadata.getMetadataMap = getMetadataMap;
function setMetadata(symbol, target, value) {
Object.defineProperty(rootMetadata(target), symbol, {
writable: true,
enumerable: true,
value
});
}
Metadata.setMetadata = setMetadata;
function hasMetadata(symbol, target) {
return !!getMetadata(symbol, target); // TODO
}
Metadata.hasMetadata = hasMetadata;
function hasOwnMetadata(symbol, target) {
return !!getOwnMetadata(symbol, target);
}
Metadata.hasOwnMetadata = hasOwnMetadata;
function getMetadata(symbol, target) {
const metadata = getOwnMetadata(symbol, target);
if (!metadata && target.prototype) {
return getMetadata(symbol, target.prototype);
}
return metadata;
}
Metadata.getMetadata = getMetadata;
function getOwnMetadata(symbol, target) {
const descriptor = Object.getOwnPropertyDescriptor(rootMetadata(target), symbol);
return descriptor ? descriptor.value : undefined;
}
Metadata.getOwnMetadata = getOwnMetadata;
function getMetadataKeys(target) {
return Object.keys(rootMetadata(target));
}
Metadata.getMetadataKeys = getMetadataKeys;
function ensureRootMetadataExists(target) {
if (!Object.getOwnPropertyDescriptor(target, LI_METADATA_ROOT)) {
Object.defineProperty(target, LI_METADATA_ROOT, {
enumerable: false,
writable: true,
value: Object.create({})
});
}
}
function rootMetadata(target) {
ensureRootMetadataExists(target);
return Object.getOwnPropertyDescriptor(target, LI_METADATA_ROOT).value;
}
})(Metadata || (Metadata = {}));
var ComponentStateMetadata;
(function (ComponentStateMetadata) {
ComponentStateMetadata.MANAGED_PROPERTY_LIST_KEY = "MANAGED_PROPERTY_LIST_KEY";
const ManagedPropertyListSymbol = Symbol("ManagedPropertyList");
function GetOwnManagedPropertyList(target) {
return Metadata.requireOwnMetadata(ManagedPropertyListSymbol, target, []);
}
ComponentStateMetadata.GetOwnManagedPropertyList = GetOwnManagedPropertyList;
function GetInheritedManagedPropertyList(target) {
const targetMetadata = GetOwnManagedPropertyList(target).slice(0);
const targetPrototype = Object.getPrototypeOf(target);
if (targetPrototype) {
targetMetadata.push(...GetInheritedManagedPropertyList(targetPrototype));
}
return targetMetadata;
}
ComponentStateMetadata.GetInheritedManagedPropertyList = GetInheritedManagedPropertyList;
function SetManagedPropertyList(target, list) {
Metadata.setMetadata(ManagedPropertyListSymbol, target, list);
}
ComponentStateMetadata.SetManagedPropertyList = SetManagedPropertyList;
function AddManagedProperty(target, property) {
SetManagedPropertyList(target, GetOwnManagedPropertyList(target).concat([property]));
}
ComponentStateMetadata.AddManagedProperty = AddManagedProperty;
})(ComponentStateMetadata || (ComponentStateMetadata = {}));
function asyncStateKey(key) {
return `${key}$`;
}
var CommonMetadata;
(function (CommonMetadata) {
CommonMetadata.MANAGED_ONDESTROY_KEY = "__LI__MANAGED__ONDESTROY__";
CommonMetadata.MANAGED_INSTANCE_DESTROYED_KEY = "__LI__MANAGED__INSTANCE__DESTROYED__";
function instanceIsDestroyed(componentInstance) {
return !!Metadata.getOwnMetadata(CommonMetadata.MANAGED_INSTANCE_DESTROYED_KEY, componentInstance);
}
CommonMetadata.instanceIsDestroyed = instanceIsDestroyed;
})(CommonMetadata || (CommonMetadata = {}));
/** @deprecated */
var EmitterMetadata;
(function (EmitterMetadata) {
EmitterMetadata.BOOTSTRAPPED_KEY = "$$STATEEMITTER_BOOTSTRAPPED";
let ProxyMode;
(function (ProxyMode) {
ProxyMode.None = "None";
ProxyMode.From = "From";
ProxyMode.Alias = "Alias";
ProxyMode.Merge = "Merge";
})(ProxyMode = EmitterMetadata.ProxyMode || (EmitterMetadata.ProxyMode = {}));
let SubjectInfo;
(function (SubjectInfo) {
function IsDynamicAlias(subjectInfo) {
return !IsStaticAlias(subjectInfo);
}
SubjectInfo.IsDynamicAlias = IsDynamicAlias;
function IsStaticAlias(subjectInfo) {
return (subjectInfo.observable instanceof Subject);
}
SubjectInfo.IsStaticAlias = IsStaticAlias;
function IsSelfProxy(subjectInfo) {
return subjectInfo.proxyPath === subjectInfo.propertyKey;
}
SubjectInfo.IsSelfProxy = IsSelfProxy;
})(SubjectInfo = EmitterMetadata.SubjectInfo || (EmitterMetadata.SubjectInfo = {}));
EmitterMetadata.EmitterMapSymbol = Symbol("EmitterMapSymbol");
/** @description Gets the metadata map object for the given target class (or its inheritted classes). */
function GetMetadataMap(target) {
return Metadata.requireMetadata(EmitterMetadata.EmitterMapSymbol, target, new Map());
}
EmitterMetadata.GetMetadataMap = GetMetadataMap;
/** @description Gets the metadata map object for the given target class. */
function GetOwnMetadataMap(target) {
return Metadata.requireOwnMetadata(EmitterMetadata.EmitterMapSymbol, target, new Map());
}
EmitterMetadata.GetOwnMetadataMap = GetOwnMetadataMap;
function HasOwnMetadataMap(target) {
return Metadata.hasOwnMetadata(EmitterMetadata.EmitterMapSymbol, target);
}
EmitterMetadata.HasOwnMetadataMap = HasOwnMetadataMap;
function SetMetadataMap(target, map) {
Metadata.setMetadata(EmitterMetadata.EmitterMapSymbol, target, map);
}
EmitterMetadata.SetMetadataMap = SetMetadataMap;
/** @description Copy all metadata from the source map to the target map.
*
* Note: This mutates the target map.
**/
function CopyMetadata(target, source, overwrite) {
// Iterate over all source metadata properties...
source.forEach((subjectInfo, eventType) => {
// And add them to this class' metadata map if not already defined
if (overwrite || !target.has(eventType)) {
target.set(eventType, Object.assign({}, subjectInfo));
}
});
return target;
}
EmitterMetadata.CopyMetadata = CopyMetadata;
/** @description Merge own and inheritted metadata into a single map.
*
* Note: This mutates the object's metadata.
**/
function CopyInherittedMetadata(object) {
if (object) {
let metadataMap = GetMetadataMap(object);
let inherittedMap = CopyInherittedMetadata(Object.getPrototypeOf(object));
return CopyMetadata(metadataMap, inherittedMap);
}
return new Map();
}
EmitterMetadata.CopyInherittedMetadata = CopyInherittedMetadata;
})(EmitterMetadata || (EmitterMetadata = {}));
var EventMetadata;
(function (EventMetadata) {
EventMetadata.SUBJECT_TABLE_MERGED_KEY = "$$EVENTSOURCE_SUBJECT_TABLE_MERGED";
EventMetadata.BOOTSTRAPPED_KEY = "$$EVENTSOURCE_BOOTSTRAPPED";
EventMetadata.LIFECYCLE_REGISTRATION_KEY = "$$EVENTSOURCE_LIFECYCLE_REGISTRATION";
const EventSubjectTableSymbol = Symbol("EventSubjectTableSymbol");
const InstanceBootstrapMapSymbol = Symbol("InstanceBootstrapMapSymbol");
const LifecycleRegistrationMapSymbol = Symbol("LifecycleRegistrationMapSymbol");
const LifecycleCallbackMapSymbol = Symbol("LifecycleCallbackMapSymbol");
/** @description Gets the metadata map object for the given target class (or its inheritted classes). */
function GetEventSubjectTable(target) {
return Metadata.requireMetadata(EventSubjectTableSymbol, target, new Map());
}
EventMetadata.GetEventSubjectTable = GetEventSubjectTable;
/** @description Gets the metadata map object for the given target class. */
function GetOwnEventSubjectTable(target) {
return Metadata.requireOwnMetadata(EventSubjectTableSymbol, target, new Map());
}
EventMetadata.GetOwnEventSubjectTable = GetOwnEventSubjectTable;
function GetInstanceBootstrapMap(target) {
return Metadata.requireMetadata(InstanceBootstrapMapSymbol, target, new Map());
}
EventMetadata.GetInstanceBootstrapMap = GetInstanceBootstrapMap;
function GetOwnLifecycleRegistrationMap(target) {
return Metadata.requireOwnMetadata(LifecycleRegistrationMapSymbol, target, new Map());
}
EventMetadata.GetOwnLifecycleRegistrationMap = GetOwnLifecycleRegistrationMap;
/** @description Gets own and inherited lifecycle registration data merged into a single Map. */
function GetLifecycleRegistrationMap(target) {
const metadata = new Map();
const ownMetadata = GetOwnLifecycleRegistrationMap(target);
ownMetadata.forEach((v, k) => metadata.set(k, v));
const targetPrototype = Object.getPrototypeOf(target);
if (targetPrototype) {
const inherittedMetadata = GetLifecycleRegistrationMap(targetPrototype);
inherittedMetadata.forEach((v, k) => metadata.set(k, v ?? metadata.get(k)));
}
return metadata;
}
EventMetadata.GetLifecycleRegistrationMap = GetLifecycleRegistrationMap;
function GetOwnLifecycleCallbackMap(target) {
return Metadata.requireOwnMetadata(LifecycleCallbackMapSymbol, target, new Map());
}
EventMetadata.GetOwnLifecycleCallbackMap = GetOwnLifecycleCallbackMap;
/** @description Gets own and inherited lifecycle callback data merged into a single Map. */
function GetLifecycleCallbackMap(target) {
const metadata = new Map();
const ownMetadata = GetOwnLifecycleCallbackMap(target);
ownMetadata.forEach((v, k) => {
if (!metadata.has(k)) {
metadata.set(k, []);
}
metadata.get(k).push(...v);
});
const targetPrototype = Object.getPrototypeOf(target);
if (targetPrototype) {
const inherittedMetadata = GetLifecycleCallbackMap(targetPrototype);
inherittedMetadata.forEach((v, k) => {
if (!metadata.has(k)) {
metadata.set(k, []);
}
metadata.get(k).push(...v);
});
}
return metadata;
}
EventMetadata.GetLifecycleCallbackMap = GetLifecycleCallbackMap;
/**
* @description
* Gets the property subject map for the given event type from the metadata map for the given target class (or its inheritted classes).
*/
function GetPropertySubjectMap(type, target) {
let table = GetEventSubjectTable(target);
let subjectMap = table.get(type);
if (!subjectMap) {
subjectMap = new Map();
table.set(type, subjectMap);
}
return subjectMap;
}
EventMetadata.GetPropertySubjectMap = GetPropertySubjectMap;
/**
* @description
* Gets the property subject map for the given event type from the metadata map for the given target class.
*/
function GetOwnPropertySubjectMap(type, target) {
let table = GetOwnEventSubjectTable(target);
let subjectMap = table.get(type);
if (!subjectMap) {
subjectMap = new Map();
table.set(type, subjectMap);
}
return subjectMap;
}
EventMetadata.GetOwnPropertySubjectMap = GetOwnPropertySubjectMap;
function GetLifecycleCallbackList(target, type) {
const map = GetLifecycleCallbackMap(target);
return map.get(type) ?? [];
}
EventMetadata.GetLifecycleCallbackList = GetLifecycleCallbackList;
function HasOwnEventSubjectTable(target) {
return Metadata.hasOwnMetadata(EventSubjectTableSymbol, target);
}
EventMetadata.HasOwnEventSubjectTable = HasOwnEventSubjectTable;
function SetEventSubjectTable(target, map) {
Metadata.setMetadata(EventSubjectTableSymbol, target, map);
}
EventMetadata.SetEventSubjectTable = SetEventSubjectTable;
function AddLifecycleCallback(target, type, callback) {
const map = GetOwnLifecycleCallbackMap(target);
if (!map.has(type)) {
map.set(type, []);
}
const callbacks = map.get(type);
callbacks.push(callback);
Metadata.setMetadata(LifecycleCallbackMapSymbol, target, map);
}
EventMetadata.AddLifecycleCallback = AddLifecycleCallback;
function RemoveLifecycleCallback(target, type, callback) {
const map = GetOwnLifecycleCallbackMap(target);
if (!map.has(type)) {
return;
}
const callbacks = map.get(type);
map.set(type, callbacks.filter(curCallback => curCallback !== callback));
Metadata.setMetadata(LifecycleCallbackMapSymbol, target, map);
}
EventMetadata.RemoveLifecycleCallback = RemoveLifecycleCallback;
/**
* @description Copy all metadata from the source map to the target map.
*
* Note: This mutates the target map.
**/
function CopySubjectTable(target, source, overwrite) {
// Iterate over all source metadata properties...
source.forEach((propertySubjectMap, eventType) => propertySubjectMap.forEach((value, propertyKey) => {
let targetPropertySubjectMap;
// Get the property subject map (or create it if it doesn't exist for this eventType)
if (target.has(eventType)) {
targetPropertySubjectMap = target.get(eventType);
}
else {
targetPropertySubjectMap = new Map();
target.set(eventType, targetPropertySubjectMap);
}
// And add them to this class' metadata map if not already defined
if (overwrite || !targetPropertySubjectMap.has(propertyKey)) {
targetPropertySubjectMap.set(propertyKey, Object.assign({}, value));
}
}));
return target;
}
EventMetadata.CopySubjectTable = CopySubjectTable;
/**
* @description Merge own and inheritted metadata into a single map.
*
* Note: This mutates the object's metadata.
**/
function CopyInherittedSubjectTable(object) {
if (object) {
let subjectTable = GetEventSubjectTable(object);
let inherittedTable = CopyInherittedSubjectTable(Object.getPrototypeOf(object));
// Merge own and inheritted metadata into a single map (note: this mutates object's metadata)
return CopySubjectTable(subjectTable, inherittedTable);
}
return new Map();
}
EventMetadata.CopyInherittedSubjectTable = CopyInherittedSubjectTable;
})(EventMetadata || (EventMetadata = {}));
/** @PropertyDecoratorFactory */
function AsyncState(asyncSource) {
/** @PropertyDecorator */
return function (target, key) {
const asyncKey = (asyncSource ?? asyncStateKey(key));
ComponentStateMetadata.AddManagedProperty(target.constructor, { key, asyncSource: asyncKey });
};
}
var AutoPush;
(function (AutoPush) {
const CHANGE_DETECTOR_DATA = Symbol("cdRefData");
let ChangeDetectorProxy;
(function (ChangeDetectorProxy) {
function fromRef(ref, options) {
return {
doCheck() {
if (options.forceDetectChanges) {
ref.detectChanges();
}
else {
ref.markForCheck();
}
}
};
}
ChangeDetectorProxy.fromRef = fromRef;
})(ChangeDetectorProxy = AutoPush.ChangeDetectorProxy || (AutoPush.ChangeDetectorProxy = {}));
function changeDetector(component) {
const metadata = changeDetectorMetadata(component);
return metadata ? metadata.changeDetector : undefined;
}
AutoPush.changeDetector = changeDetector;
function enable(component, changeDetector, options = {}) {
Metadata.setMetadata(CHANGE_DETECTOR_DATA, component, {
options,
changeDetector: isProxy(changeDetector) ? changeDetector : ChangeDetectorProxy.fromRef(changeDetector, options)
});
}
AutoPush.enable = enable;
function notifyChanges(component) {
// Check to see if AutoPush is enabled on this component
const cdData = changeDetectorMetadata(component);
if (cdData) {
// Notify change detector that there were changes to a component value
cdData.changeDetector.doCheck();
}
}
AutoPush.notifyChanges = notifyChanges;
function isChangeDetectorLike(object) {
return object && typeof object.detectChanges === "function";
}
AutoPush.isChangeDetectorLike = isChangeDetectorLike;
function changeDetectorMetadata(component) {
return Metadata.getMetadata(CHANGE_DETECTOR_DATA, component);
}
function isProxy(input) {
return input && typeof input.doCheck === "function";
}
})(AutoPush || (AutoPush = {}));
// Enable dynamic templating for Ivy-compiled components:
/** @deprecated */
function TemplateDynamic() {
return class TemplateDynamic {
};
}
/** @deprecated */
class LiComponent extends TemplateDynamic() {
}
// TODO fix generics when TypeScript mixin issue is fixed: https://github.com/Microsoft/TypeScript/issues/24122
function ManagedObservableWrapper /*<T, BaseObservable extends Observable<T>>*/($class) {
class _Managed extends $class {
componentInstance;
subscriptions = new Subscription();
constructor(componentInstance, ...args) {
super(...args);
this.componentInstance = componentInstance;
// Automatically handle unsubscribing on component's ngOnDestroy event
this.subscriptions.add(componentInstance[CommonMetadata.MANAGED_ONDESTROY_KEY].subscribe(() => {
// Mark the component instance as destroyed
Metadata.setMetadata(CommonMetadata.MANAGED_INSTANCE_DESTROYED_KEY, this.componentInstance, true);
this.subscriptions?.unsubscribe();
this.subscriptions = undefined;
this.componentInstance = undefined;
if (this instanceof Subject) {
this.complete();
}
}));
}
subscribe(...args) {
if (this.componentInstance && !CommonMetadata.instanceIsDestroyed(this.componentInstance)) {
const subscription = super.subscribe(...args);
// Manage new subscription
this.subscriptions.add(subscription);
return subscription;
}
else {
return EMPTY.subscribe();
}
}
}
;
return _Managed;
}
class ManagedObservable extends ManagedObservableWrapper(Observable) {
constructor(componentInstance, subscribe) {
super(componentInstance, subscribe);
}
}
class ManagedSubject extends ManagedObservableWrapper(Subject) {
constructor(componentInstance) {
super(componentInstance);
}
}
class ManagedBehaviorSubject extends ManagedObservableWrapper(BehaviorSubject) {
constructor(componentInstance, initialValue) {
super(componentInstance, initialValue);
}
}
class ManagedReplaySubject extends ManagedObservableWrapper(ReplaySubject) {
constructor(componentInstance, bufferSize, windowTime, scheduler) {
super(componentInstance, bufferSize, windowTime, scheduler);
}
}
var AngularLifecycleType;
(function (AngularLifecycleType) {
AngularLifecycleType["OnChanges"] = "ngOnChanges";
AngularLifecycleType["OnInit"] = "ngOnInit";
AngularLifecycleType["OnDestroy"] = "ngOnDestroy";
AngularLifecycleType["DoCheck"] = "ngDoCheck";
AngularLifecycleType["AfterContentInit"] = "ngAfterContentInit";
AngularLifecycleType["AfterContentChecked"] = "ngAfterContentChecked";
AngularLifecycleType["AfterViewInit"] = "ngAfterViewInit";
AngularLifecycleType["AfterViewChecked"] = "ngAfterViewChecked";
})(AngularLifecycleType || (AngularLifecycleType = {}));
;
(function (AngularLifecycleType) {
AngularLifecycleType.values = [
AngularLifecycleType.OnChanges,
AngularLifecycleType.OnInit,
AngularLifecycleType.OnDestroy,
AngularLifecycleType.DoCheck,
AngularLifecycleType.AfterContentInit,
AngularLifecycleType.AfterContentChecked,
AngularLifecycleType.AfterViewInit,
AngularLifecycleType.AfterViewChecked
];
})(AngularLifecycleType || (AngularLifecycleType = {}));
/** @PropertyDecoratorFactory */
function EventSource(...args) {
let paramsArg;
if (args.length > 0) {
paramsArg = args[0];
}
if (!paramsArg || paramsArg instanceof Function) {
return EventSource.WithParams(undefined, ...args);
}
else {
return EventSource.WithParams(paramsArg, ...args.slice(1));
}
}
(function (EventSource) {
/** @PropertyDecoratorFactory */
function WithParams(options, ...methodDecorators) {
options ??= {};
/** @PropertyDecorator */
return function (target, propertyKey) {
if (propertyKey !== CommonMetadata.MANAGED_ONDESTROY_KEY && !options.unmanaged) {
// Ensure that we create a ngOnDestroy EventSource on the target for managing subscriptions
WithParams({ eventType: AngularLifecycleType.OnDestroy })(target, CommonMetadata.MANAGED_ONDESTROY_KEY);
}
// If an eventType wasn't specified...
if (!options.eventType) {
// Try to deduce the eventType from the propertyKey
if (typeof propertyKey === "string" && propertyKey.endsWith("$")) {
options.eventType = propertyKey.substring(0, propertyKey.length - 1);
}
else {
throw new Error(`@EventSource error: eventType could not be deduced from propertyKey "${propertyKey}" (only keys ending with '$' can be auto-deduced).`);
}
}
// Create the event source metadata for the decorated property
createMetadata(options, target, propertyKey);
// Apply any method decorators to the facade function
methodDecorators.forEach(methodDecorator => methodDecorator(target, options.eventType, Object.getOwnPropertyDescriptor(target, options.eventType)));
};
}
EventSource.WithParams = WithParams;
function bootstrapInstance(eventType, isLifecycleEvent) {
const targetInstance = this;
if (!isLifecycleEvent) {
// Assign the facade function for the given event type to the target instance
Facade.CreateAndAssign(eventType, targetInstance);
}
function classSubjectTableMerged(merged) {
if (merged === undefined) {
return !!Metadata.getMetadata(EventMetadata.SUBJECT_TABLE_MERGED_KEY, targetInstance);
}
else {
Metadata.setMetadata(EventMetadata.SUBJECT_TABLE_MERGED_KEY, targetInstance, merged);
}
return undefined;
}
const subjectTable = EventMetadata.GetOwnEventSubjectTable(targetInstance);
if (!classSubjectTableMerged()) {
// Copy all event metadata from the class constructor to the target instance
EventMetadata.CopySubjectTable(subjectTable, EventMetadata.CopyInherittedSubjectTable(targetInstance.constructor), true);
classSubjectTableMerged(true);
}
const propertySubjectMap = subjectTable.get(eventType);
// Iterate over each of the target properties for each proxied event type used in this class
propertySubjectMap?.forEach((subjectInfo, propertyKey) => {
// If the event proxy subject hasn't been created for this property yet...
if (!subjectInfo.subject) {
// Create a new Subject
if (subjectInfo.unmanaged || propertyKey === CommonMetadata.MANAGED_ONDESTROY_KEY) {
subjectInfo.subject = new Subject();
}
else {
subjectInfo.subject = new ManagedSubject(targetInstance);
}
}
// Set the property key to a function that will invoke the facade method when called
// (This is needed to allow EventSources to work with Angular event decorators like @HostListener)
// Compose the function with the observable
// TODO - Figure out a better way to do this with Ivy
let propertyValue = Object.setPrototypeOf(Facade.Create(eventType), subjectInfo.subject);
Object.defineProperty(targetInstance, propertyKey, {
get: () => propertyValue
});
});
EventMetadata.GetInstanceBootstrapMap(targetInstance).set(eventType, true);
}
let Facade;
(function (Facade) {
/** @description
* Creates an event facade function (the function that is invoked during an event) for the given event type.
*/
function Create(eventType) {
return Object.assign(function (...values) {
// Get the list of subjects to notify for this `eventType`
const subjectInfoList = Array.from(EventMetadata.GetPropertySubjectMap(eventType, this).values());
// Use the first value from this event if only a single value was given, otherwise emit all given values as an array to the Subject
const valueToEmit = (values.length > 1) ? values : (values.length > 0) ? values[0] : undefined;
// Iterate in reverse order for ngOnDestroy eventTypes.
// This ensures that all user-defined OnDestroy EventSources are fired before final cleanup of subscriptions.
if (eventType === "ngOnDestroy") {
subjectInfoList.reverse();
}
// Emit the given event value to each interested subject
subjectInfoList
.filter(subjectInfo => !!subjectInfo.subject)
.forEach(subjectInfo => subjectInfo.subject.next(valueToEmit));
}, { eventType });
}
Facade.Create = Create;
function CreateAndAssign(eventType, instance) {
// Assign the facade function for the given event type to the appropriate target class method
// This function gets called from the view template and triggers the associated Subject
Object.defineProperty(instance, eventType, {
enumerable: true,
value: Create(eventType)
});
}
Facade.CreateAndAssign = CreateAndAssign;
})(Facade || (Facade = {}));
function createMetadata(options, target, propertyKey) {
const ContainsCustomMethod = ($class = target) => {
const methodDescriptor = Object.getOwnPropertyDescriptor($class, options.eventType);
const method = methodDescriptor ? (methodDescriptor.value || methodDescriptor.get) : undefined;
const isCustomMethod = method && method.eventType !== options.eventType;
return isCustomMethod || (!method && target.prototype && ContainsCustomMethod(target.prototype));
};
// Determine if this EventSource is handling an Angular lifecycle event
const isLifecycleEvent = AngularLifecycleType.values.includes(options.eventType);
if (!options.skipMethodCheck && ContainsCustomMethod()) {
// Make sure the target class doesn't have a custom method already defined for this event type
throw new Error(`@EventSource metadata creation failed. Class already has a custom ${options.eventType} method.`);
}
// Add ths EventSource definition to the class' metadata
EventMetadata.GetOwnPropertySubjectMap(options.eventType, target.constructor).set(propertyKey, options);
if (isLifecycleEvent) {
registerLifecycleEventFacade(target.constructor, options.eventType);
}
// Initialize the propertyKey on the target to a self-bootstrapper that will initialize an instance's EventSource when called
Object.defineProperty(target, propertyKey, {
configurable: true,
get: function () {
// Ensure we only bootstrap once for this `eventType` if the intializer is re-invoked (Ivy)
if (!isBootstrapped.call(this, options.eventType)) {
// Boostrap the event source for this instance
bootstrapInstance.bind(this)(options.eventType, isLifecycleEvent);
}
// Return the Observable for the event
return this[propertyKey];
}
});
// Only initialize a bootstrapper function for the eventType if this isn't a lifecycle event (otherwise Ivy will handle it)
if (!isLifecycleEvent) {
// Set the eventType on the target to a self-bootstrapper function that will initialize an instance's EventSource when called
Object.defineProperty(target, options.eventType, {
configurable: true,
writable: true,
value: Object.assign(function (...args) {
// Ensure we only bootstrap once for this `eventType` if the intializer is re-invoked (Ivy)
if (!isBootstrapped.call(this, options.eventType)) {
// Boostrap the event source for this instance
bootstrapInstance.bind(this)(options.eventType);
}
// Invoke the facade function for the event
return this[options.eventType].call(this, ...args);
}, { eventType: options.eventType })
});
}
}
function registerLifecycleEventFacade(targetClass, eventType) {
const registrationMap = EventMetadata.GetLifecycleRegistrationMap(targetClass);
// Register the facade function for this component lifecycle target if we haven't already
if (!registrationMap.get(eventType)) {
const ownRegistrationMap = EventMetadata.GetOwnLifecycleRegistrationMap(targetClass);
registerLifecycleEvent(targetClass, eventType, Facade.Create(eventType));
ownRegistrationMap.set(eventType, true);
}
}
/**
* @description Registers a lifecycle event handler for use with `registerPreOrderHooks`/`registerPostOrderHooks`
*/
function registerLifecycleEvent(targetClass, eventType, hookFn) {
EventMetadata.AddLifecycleCallback(targetClass, eventType, hookFn);
// Ensure a valid prototype exists for this component
if (!targetClass.prototype) {
targetClass.prototype = Object.create({});
}
// Get the name of the hook for this lifecycle event
const hookName = eventType;
// Store a reference to the original hook function
const prevLifecycleHook = targetClass.prototype[hookName];
// Replace the default lifecycle hook with a modified one that ensures the given hook fns are invoked
if (!prevLifecycleHook?.eventType) {
targetClass.prototype[hookName] = Object.assign(function (...args) {
// Call the previous hook function on the component instance if there is one
if (prevLifecycleHook) {
prevLifecycleHook.call(this, ...args);
}
// Invoke all of the hook functions associated with this lifeycle event for the current component instance
const hookFns = EventMetadata.GetLifecycleCallbackList(this.constructor, eventType);
hookFns.forEach(hookFn => hookFn.call(this, ...args));
}, { eventType });
}
}
EventSource.registerLifecycleEvent = registerLifecycleEvent;
function unregisterLifecycleEvent(targetClass, eventType, hookFn) {
EventMetadata.RemoveLifecycleCallback(targetClass, eventType, hookFn);
}
EventSource.unregisterLifecycleEvent = unregisterLifecycleEvent;
function isBootstrapped(eventType) {
const map = EventMetadata.GetInstanceBootstrapMap(this);
return map.has(eventType) ? map.get(eventType) : false;
}
})(EventSource || (EventSource = {}));
const COMPONENT_STATE_IDENTITY = Symbol("COMPONENT_STATE_IDENTITY");
class ComponentStateRef extends Promise {
componentInstance;
/**
* @description Resolves the `ComponentState` instance for this reference.
* @returns An `Observable` that emits the `ComponentState` instance for this reference.
*/
state() {
return from(this);
}
/**
* @description Returns an `Observable` that represents the current value of the given state property and emits whenever the value of the given state
* property is changed.
* @param stateProp - The state property to observe.
* @returns An `Observable` that emits the value of the given state property and re-emits when the value is changed.
*/
get(stateProp) {
const stateKey = ComponentState.stateKey(stateProp);
const resolvedSource$ = this.resolvedState?.[stateKey];
if (resolvedSource$) {
return resolvedSource$;
}
else {
return this.state().pipe(mergeMap((state) => {
if (!state[stateKey]) {
return throwError(`[ComponentStateRef] Failed to get state for component property "${stateProp}". Ensure that this property is explicitly initialized (or declare it with @DeclareState()).`);
}
return state[stateKey];
}));
}
}
/**
* @description Returns an array of `Observable`s that represents the current value for each given state property. Each `Observable` emits whenever a
* value of the corresponding given state property is changed.
* @param stateProps - The state properties to observe.
* @returns An array of `Observable`s that represents the current value for each given state property and re-emits when the corresponding value is
* changed.
*/
getAll(...stateProps) {
return stateProps.map(stateProp => this.get(stateProp));
}
/**
* @description Returns an `EventEmitter` that emits whenever the value of the given state property is changed.
* @param stateProp - The state property to observe.
* @returns An `EventEmitter` instance that emits whenever the value of the given state property is changed.
*/
emitter(stateProp) {
const emitter$ = new EventEmitter();
this.get(stateProp)
.pipe(skip(1))
.subscribe({
next: value => emitter$.emit(value),
error: err => emitter$.error(err)
});
return emitter$;
}
/**
* @description Updates the value of the given state property with the given value. Equivalent to assigning to the component state property directly.
* @param stateProp - The state property to update. This property must not be readonly.
* @param value - The new value to update to.
* @returns An `Observable` that emits and completes when the value has been updated.
*/
set(stateProp, value) {
const stateKey = ComponentState.stateKey(stateProp);
const result$ = new ReplaySubject(1);
const resolvedSource$ = this.resolvedState?.[stateKey];
if (resolvedSource$) {
resolvedSource$.next(value);
result$.next();
result$.complete();
}
else {
this.state().pipe(map((state) => {
const stateSubject$ = state[ComponentState.stateKey(stateProp)];
if (!stateSubject$) {
throw new Error(`[ComponentStateRef] Failed to set state for component property "${stateProp}". Ensure that this property is explicitly initialized (or declare it with @DeclareState()).`);
}
return stateSubject$;
})).subscribe((stateSubject$) => {
stateSubject$.next(value);
result$.next();
result$.complete();
}, (e) => {
result$.error(e);
result$.complete();
}, () => result$.complete());
}
return result$;
}
/**
* @description Subscribes the given state property to the given source `Observable`. If `managed` is set to true, the lifetime of the subscription will
* be managed and cleaned up when the component is destroyed.
* @param stateProp - The state property to receive source updates. This property must not be readonly.
* @param source$ - The source `Observable` to subscribe to.
* @param managed - Whether or not the subscription lifetime should be managed. Defaults to `true`.
* @returns A `Subscription` representing the subscription to the source.
*/
subscribeTo(stateProp, source$, managed = true) {
let managedSource$;
if (managed) {
managedSource$ = this.state().pipe(mergeMap(() => _createManagedSource(source$, this.componentInstance)));
}
else {
managedSource$ = source$;
}
return managedSource$.pipe(tap(sourceValue => this.set(stateProp, sourceValue))).subscribe();
}
/**
* @description Synchronizes the values of the given state properties such that any changes from one state property will be propagated to the
* other state property. The initial value of the first given state property is used.
* @param statePropA - The first state property to synchronize. This property must not be readonly.
* @param statePropB - The second state property to synchronize. This property must not be readonly.
*/
sync(statePropA, statePropB) {
let syncing = false;
merge(this.get(statePropB), this.get(statePropA)).pipe(skip(1), distinctUntilChanged(), filter(() => !syncing), tap(() => syncing = true), mergeMap((value) => combineLatest([
this.set(statePropA, value),
this.set(statePropB, value)
])), tap(() => syncing = false)).subscribe();
}
syncWith(stateProp, source, sourceProp) {
let syncing = false;
this.state().pipe(switchMap(() => merge(this.get(stateProp).pipe(skip(1)), source instanceof Subject
? _createManagedSource(source, this.componentInstance)
: source.get(sourceProp))), distinctUntilChanged(), filter(() => !syncing), tap(() => syncing = true), mergeMap((value) => {
return forkJoin([
source instanceof Subject
? of(source.next(value))
: source.set(sourceProp, value),
this.set(stateProp, value)
]);
}), tap(() => syncing = false)).subscribe();
}
get resolvedState() {
return this.componentInstance?.[COMPONENT_STATE_IDENTITY];
}
}
var ComponentState;
(function (ComponentState) {
function create($class, options) {
return createComponentState($class, options);
}
ComponentState.create = create;
function createFactory($class, options) {
options ??= {
lazy: isForwardRef($class)
};
if (!options.lazy) {
if (isForwardRef($class)) {
throw new Error("[ComponentState] A component state created with forwardRef must be created with the `lazy` flag.");
}
const resolvedClass = resolveClass($class);
// Generate initial component state on ngOnInit
updateStateOnEvent(resolvedClass, AngularLifecycleType.OnInit);
// Update the component state on afterViewInit and afterContentInit to capture dynamically initialized properties
updateStateOnEvent(resolvedClass, AngularLifecycleType.AfterContentInit);
updateStateOnEvent(resolvedClass, AngularLifecycleType.AfterViewInit);
}
return function (injector) {
const stateRef = new ComponentStateRef((resolve) => {
const resolvedClass = resolveClass($class);
const delayedInitializer = setTimeout(() => {
// If the stateRef has not been initialized by the end of the current execution frame (e.g. the service was
// injected after component's lifecycle events were invoked), we need to resolve it now.
const instance = injector.get(resolvedClass);
stateRef.componentInstance = instance;
updateState(_requireComponentState(instance), instance);
// Resolve the component state
resolve(instance[COMPONENT_STATE_IDENTITY]);
});
if (options.lazy) {
// Generate initial component state on ngOnInit
updateStateOnEvent(resolvedClass, AngularLifecycleType.OnInit, injector, (instance) => {
clearTimeout(delayedInitializer);
stateRef.componentInstance = instance;
});
// Update the component state on afterViewInit and afterContentInit to capture dynamically initialized properties
updateStateOnEvent(resolvedClass, AngularLifecycleType.AfterContentInit, injector);
updateStateOnEvent(resolvedClass, AngularLifecycleType.AfterViewInit, injector, (instance) => {
clearTimeout(delayedInitializer);
// Resolve the component state
resolve(instance[COMPONENT_STATE_IDENTITY]);
});
}
else {
updateOnEvent(resolvedClass, AngularLifecycleType.OnInit, (instance) => {
clearTimeout(delayedInitializer);
stateRef.componentInstance = instance;
}, injector);
updateOnEvent(resolvedClass, AngularLifecycleType.AfterViewInit, (instance) => {
clearTimeout(delayedInitializer);
// Resolve the component state
resolve(instance[COMPONENT_STATE_IDENTITY]);
}, injector);
}
});
return stateRef;
};
}
ComponentState.createFactory = createFactory;
function tokenFor(provider) {
return stateTokenFor(provider);
}
ComponentState.tokenFor = tokenFor;
function stateKey(key) {
return asyncStateKey(key);
}
ComponentState.stateKey = stateKey;
function updateStateOnEvent($class, event, injector, onComplete) {
updateOnEvent($class, event, (instance) => {
updateState(_requireComponentState(instance), instance);
if (onComplete) {
onComplete(instance);
}
}, injector);
}
function updateOnEvent($class, event, onUpdate, injector) {
onEvent($class, event, function onEventFn() {
const instance = injector ? injector.get($class, null, { self: true }) : this;
if (instance === this) {
onUpdate(instance);
// Only de-register instance-specific event handlers
if (injector) {
offEvent($class, event, onEventFn);
}
}
});
}
function onEvent($class, event, callback) {
// Ensure that we create a OnDestroy EventSource on the target for managing subscriptions
EventSource({ eventType: AngularLifecycleType.OnDestroy })($class.prototype, CommonMetadata.MANAGED_ONDESTROY_KEY);
// Register a lifecycle event listener for the given event
EventSource.registerLifecycleEvent($class, event, callback);
}
function offEvent($class, event, callback) {
EventSource.unregisterLifecycleEvent($class, event, callback);
}
function updateState(componentState, instance) {
const instanceProps = getAllAccessibleKeys(instance);
// Create a managed reactive state wrapper for each component property
instanceProps.forEach((prop) => {
// Only update an entry if it hasn't yet been defined
if (!componentState[ComponentState.stateKey(prop.key)]) {
updateStateForProperty(componentState, instance, prop);
}
});
return componentState;
}
function updateStateForProperty(componentState, instance, prop) {
const propDescriptor = Object.getOwnPropertyDescriptor(instance, prop.key);
const stateSubjectProp = stateKey(prop.key);
if (typeof prop.key === "string" && !prop.key.endsWith("$") && !EmitterMetadata.GetMetadataMap(instance).get(prop.key)) {
if (!propDescriptor || propDescriptor.configurable) {
let lastValue = instance[prop.key];
const propSubject$ = new ManagedBehaviorSubject(instance, lastValue);
function manageProperty(instance, property, enumerable) {
const stateProp = stateKey(property);
componentState[stateProp] = propSubject$;
// Override the instance property with a getter/setter that synchronizes with `propSubject$`
Object.defineProperty(instance, property, {
configurable: true,
enumerable: enumerable,
get: () => lastValue,
set: isReadonlyProperty(instance, property) ? undefined : (newValue) => propSubject$.next(newValue)
});
}
// Monitor the property subject for value changes
propSubject$.pipe(skip(1)).subscribe(value => {
// Update the cached value
lastValue = value;
// Notify the component of changes if AutoPush is enabled
AutoPush.notifyChanges(instance);
});
if (prop.asyncSource) {
const reactiveSource$ = instance[prop.asyncSource];
// If the property has a valid async source, create a managed subscription to it
if (reactiveSource$ && reactiveSource$ instanceof Observable) {
_createManagedSource(reactiveSource$, instance)
.subscribe((value) => propSubject$.next(value));
}
}
// Set up the property wrapper that exposes the backing subject
try {
manageProperty(instance, prop.key, !propDescriptor || !!propDescriptor.enumerable);
// If a separate publicKey was defined, also map it to the backing subject
if (prop.publicKey && prop.publicKey !== prop.key) {
manageProperty(instance, prop.publicKey, true);
}
}
catch (e) {
console.error(`Failed to create state Subject for property ${instance.constructor.name}.${prop.key}`, e);
}
}
else {
if (!propDescriptor.configurable && !isReadonlyProperty(instance, prop.key)) {
console.warn(`[ComponentState] Property "${instance.constructor.name}.${prop.key}" is not configurable and will be treated as readonly.`);
}
// Property is readonly, so just use an Observable that emits the underlying state on subscription
componentState[stateSubjectProp] = new ManagedObservable(instance, observer => {
observer.next(propDescriptor.get ? propDescriptor.get() : propDescriptor.value);
});
}
}
return componentState;
}
function isReadonlyProperty(instance, key) {
const publicPropDescriptor = Object.getOwnPropertyDescriptor(instance, key);
return !!publicPropDescriptor && !publicPropDescriptor.writable && !publicPropDescriptor.set;
}
function isForwardRef($class) {
return !$class.name;
}
function resolveClass($class) {
return resolveForwardRef($class);
}
function getAllAccessibleKeys(instance) {
// Ensure managed keys are processed first
return getManagedKeys(instance).concat(getPublicKeys(instance));
}
function getPublicKeys(instance) {
re