@deepkit/desktop-ui
Version:
Library for desktop UI widgets in Angular 10+
96 lines (85 loc) • 3.57 kB
text/typescript
/*
* Deepkit Framework
* Copyright (C) 2021 Deepkit UG, Marc J. Schmidt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the MIT License.
*
* You should have received a copy of the MIT License along with this program.
*/
import { OnDestroy, ɵComponentDef as ComponentDef, ɵNG_COMP_DEF as NG_COMP_DEF } from '@angular/core';
import { getClassName } from '@deepkit/core';
const lazyValuesStore = new WeakMap<any, object>();
const lazyValuesDestroyed = new WeakMap<any, object>();
function lazyInitialize(target: any, map: WeakMap<any, object> = lazyValuesStore): any {
let object = map.get(target);
if (object) return object;
object = {};
map.set(target, object);
return object;
}
function getRealMethodHookName(value: string): string {
return 'ng' + value.substr(0, 1).toUpperCase() + value.substr(1);
}
function addComponentHook<T>(target: T, hookName: 'onDestroy' | 'onChanges' | 'onInit' | 'afterViewInit', fn: (this: T) => void) {
const cdef: ComponentDef<any> = ((target as any).constructor as any)[NG_COMP_DEF];
if (cdef) {
//prod build
const ori = (cdef as any)[hookName];
((cdef as any)['onDestroy'] as any) = function (this: any, ...args: any[]) {
fn.call(this);
ori && (ori as any).apply(this, args);
};
} else {
const ori = (target as any).constructor.prototype[getRealMethodHookName(hookName)];
(target as any).constructor.prototype[getRealMethodHookName(hookName)] = function (this: any, ...args: any[]) {
fn.call(this);
ori && (ori as any).apply(this, args);
};
}
}
/**
* Automatically unsubscribe the value (calling unsubscribe() on the current value)
* when ngOnDestroy is called or a new value has been set.
* When the component is already destroyed, newly set values will be unscubribed immediately.
* This makes sure when a component is destroyed too fast before a async operation is completed
* that the result is unsubscribed, otherwise it would be a memory leak.
*/
export function unsubscribe<T extends OnDestroy>() {
return function (target: T, propertyKey: string | symbol) {
function unsub(value: any) {
if (value && value.unsubscribe) {
try {
value.unsubscribe();
} catch (error) {
console.log('Subscription was already unsubscribed.', getClassName(target), propertyKey);
}
}
}
Object.defineProperty(target, propertyKey, {
enumerable: true,
configurable: false, //even with true the prop cant be deleted using `delete this.name`
get() {
const store = lazyInitialize(this);
return store[propertyKey];
},
set(value) {
const destroyed = lazyInitialize(this, lazyValuesDestroyed);
const store = lazyInitialize(this);
unsub(store[propertyKey]);
if (destroyed['destroyed']) {
unsub(value);
}
store[propertyKey] = value;
}
});
addComponentHook(target, 'onDestroy', function () {
const destroyed = lazyInitialize(this, lazyValuesDestroyed);
destroyed['destroyed'] = true;
const store = lazyInitialize(this);
if (store[propertyKey]) {
unsub(store[propertyKey]);
}
});
};
}