MOBILE-4612 compile: Improve destroying effects
parent
9f910d338f
commit
e8610272c7
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { toBoolean } from '@/core/transforms/boolean';
|
import { toBoolean } from '@/core/transforms/boolean';
|
||||||
|
import { effectWithInjectionContext } from '@/core/utils/signals';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
Input,
|
Input,
|
||||||
|
@ -34,6 +35,9 @@ import {
|
||||||
Type,
|
Type,
|
||||||
KeyValueDiffer,
|
KeyValueDiffer,
|
||||||
Injector,
|
Injector,
|
||||||
|
EffectRef,
|
||||||
|
EffectCleanupRegisterFn,
|
||||||
|
CreateEffectOptions,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { CorePromisedValue } from '@classes/promised-value';
|
import { CorePromisedValue } from '@classes/promised-value';
|
||||||
|
|
||||||
|
@ -212,6 +216,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||||
return class CoreCompileHtmlFakeComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
|
return class CoreCompileHtmlFakeComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
private ongoingLifecycleHooks: Set<keyof AfterViewInit | keyof AfterContentInit | keyof OnDestroy> = new Set();
|
private ongoingLifecycleHooks: Set<keyof AfterViewInit | keyof AfterContentInit | keyof OnDestroy> = new Set();
|
||||||
|
protected effectRefs: EffectRef[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Store this instance so it can be accessed by the outer component.
|
// Store this instance so it can be accessed by the outer component.
|
||||||
|
@ -221,12 +226,25 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||||
this['dataObject'] = {};
|
this['dataObject'] = {};
|
||||||
this['dataArray'] = [];
|
this['dataArray'] = [];
|
||||||
|
|
||||||
|
const effectWithContext = effectWithInjectionContext(compileInstance.injector);
|
||||||
|
|
||||||
// Inject the libraries.
|
// Inject the libraries.
|
||||||
CoreCompile.injectLibraries(
|
CoreCompile.injectLibraries(this, {
|
||||||
this,
|
extraLibraries: compileInstance.extraProviders,
|
||||||
compileInstance.extraProviders,
|
injector: compileInstance.injector,
|
||||||
compileInstance.injector,
|
// Capture calls to effect to retrieve the effectRefs and destroy them when this component is destroyed.
|
||||||
);
|
// Otherwise effects are only destroyed when the parent component is destroyed.
|
||||||
|
effectWrapper: (
|
||||||
|
effectFn: (onCleanup: EffectCleanupRegisterFn) => void,
|
||||||
|
options?: Omit<CreateEffectOptions, 'injector'>,
|
||||||
|
): EffectRef => {
|
||||||
|
const effectRef = effectWithContext(effectFn, options);
|
||||||
|
|
||||||
|
this.effectRefs.push(effectRef);
|
||||||
|
|
||||||
|
return effectRef;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Always add these elements, they could be needed on component init (componentObservable).
|
// Always add these elements, they could be needed on component init (componentObservable).
|
||||||
this['ChangeDetectorRef'] = compileInstance.changeDetector;
|
this['ChangeDetectorRef'] = compileInstance.changeDetector;
|
||||||
|
@ -280,6 +298,9 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
this.effectRefs.forEach(effectRef => effectRef.destroy());
|
||||||
|
this.effectRefs = [];
|
||||||
|
|
||||||
this.callLifecycleHookOverride('ngOnDestroy');
|
this.callLifecycleHookOverride('ngOnDestroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
signal,
|
signal,
|
||||||
computed,
|
computed,
|
||||||
|
effect,
|
||||||
untracked,
|
untracked,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
|
@ -261,20 +262,19 @@ export class CoreCompileProvider {
|
||||||
* Inject all the core libraries in a certain object.
|
* Inject all the core libraries in a certain object.
|
||||||
*
|
*
|
||||||
* @param instance The instance where to inject the libraries.
|
* @param instance The instance where to inject the libraries.
|
||||||
* @param extraLibraries Extra imported providers if needed and not imported by this class.
|
* @param options Options.
|
||||||
* @param injector Injector of the injection context. E.g. for a component, use the component's injector.
|
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
injectLibraries(instance: any, extraLibraries: Type<unknown>[] = [], injector?: Injector): void {
|
injectLibraries(instance: any, options: InjectLibrariesOptions = {}): void {
|
||||||
if (!this.libraries || !this.exportedObjects) {
|
if (!this.libraries || !this.exportedObjects) {
|
||||||
throw new CoreError('Libraries not loaded. You need to call loadLibraries before calling injectLibraries.');
|
throw new CoreError('Libraries not loaded. You need to call loadLibraries before calling injectLibraries.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const libraries = [
|
const libraries = [
|
||||||
...this.libraries,
|
...this.libraries,
|
||||||
...extraLibraries,
|
...options.extraLibraries ?? [],
|
||||||
];
|
];
|
||||||
injector = injector ?? this.injector;
|
const injector = options.injector ?? this.injector;
|
||||||
|
|
||||||
// We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
|
// We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
|
||||||
for (const i in libraries) {
|
for (const i in libraries) {
|
||||||
|
@ -305,7 +305,7 @@ export class CoreCompileProvider {
|
||||||
instance['signal'] = signal;
|
instance['signal'] = signal;
|
||||||
instance['computed'] = computed;
|
instance['computed'] = computed;
|
||||||
instance['untracked'] = untracked;
|
instance['untracked'] = untracked;
|
||||||
instance['effect'] = effectWithInjectionContext(injector);
|
instance['effect'] = options.effectWrapper ?? effectWithInjectionContext(injector);
|
||||||
instance['model'] = modelWithInjectionContext(injector);
|
instance['model'] = modelWithInjectionContext(injector);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -430,3 +430,13 @@ export class CoreCompileProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CoreCompile = makeSingleton(CoreCompileProvider);
|
export const CoreCompile = makeSingleton(CoreCompileProvider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for injectLibraries.
|
||||||
|
*/
|
||||||
|
type InjectLibrariesOptions = {
|
||||||
|
extraLibraries?: Type<unknown>[]; // Extra imported providers if needed and not imported by this class.
|
||||||
|
injector?: Injector; // Injector of the injection context. E.g. for a component, use the component's injector.
|
||||||
|
effectWrapper?: typeof effect; // Wrapper function to create an effect. If not provided, a wrapper will be created using the
|
||||||
|
// injector. Use this wrapper if you want to capture the created EffectRefs.
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue