commit
						9df5533dd7
					
				@ -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');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -24,9 +24,7 @@ import {
 | 
				
			|||||||
    signal,
 | 
					    signal,
 | 
				
			||||||
    computed,
 | 
					    computed,
 | 
				
			||||||
    effect,
 | 
					    effect,
 | 
				
			||||||
    EffectCleanupRegisterFn,
 | 
					    untracked,
 | 
				
			||||||
    CreateEffectOptions,
 | 
					 | 
				
			||||||
    EffectRef,
 | 
					 | 
				
			||||||
} from '@angular/core';
 | 
					} from '@angular/core';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ActionSheetController,
 | 
					    ActionSheetController,
 | 
				
			||||||
@ -41,6 +39,7 @@ import { TranslateService } from '@ngx-translate/core';
 | 
				
			|||||||
import { CoreLogger } from '@singletons/logger';
 | 
					import { CoreLogger } from '@singletons/logger';
 | 
				
			||||||
import { CoreEvents } from '@singletons/events';
 | 
					import { CoreEvents } from '@singletons/events';
 | 
				
			||||||
import { makeSingleton } from '@singletons';
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { effectWithInjectionContext, modelWithInjectionContext } from '@/core/utils/signals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Import core services.
 | 
					// Import core services.
 | 
				
			||||||
import { getCoreServices } from '@/core/core.module';
 | 
					import { getCoreServices } from '@/core/core.module';
 | 
				
			||||||
@ -263,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) {
 | 
				
			||||||
@ -306,15 +304,10 @@ export class CoreCompileProvider {
 | 
				
			|||||||
        instance['Md5'] = Md5;
 | 
					        instance['Md5'] = Md5;
 | 
				
			||||||
        instance['signal'] = signal;
 | 
					        instance['signal'] = signal;
 | 
				
			||||||
        instance['computed'] = computed;
 | 
					        instance['computed'] = computed;
 | 
				
			||||||
        // Create a wrapper to call effect with the proper injection context.
 | 
					        instance['untracked'] = untracked;
 | 
				
			||||||
        instance['effect'] = (
 | 
					        instance['effect'] = options.effectWrapper ?? effectWithInjectionContext(injector);
 | 
				
			||||||
            effectFn: (onCleanup: EffectCleanupRegisterFn) => void,
 | 
					        instance['model'] = modelWithInjectionContext(injector);
 | 
				
			||||||
            options?: Omit<CreateEffectOptions, 'injector'>,
 | 
					
 | 
				
			||||||
        ): EffectRef =>
 | 
					 | 
				
			||||||
            effect(effectFn, {
 | 
					 | 
				
			||||||
                ...options,
 | 
					 | 
				
			||||||
                injector,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * @deprecated since 4.1, plugins should use CoreNetwork instead.
 | 
					         * @deprecated since 4.1, plugins should use CoreNetwork instead.
 | 
				
			||||||
         * Keeping this a bit more to avoid plugins breaking.
 | 
					         * Keeping this a bit more to avoid plugins breaking.
 | 
				
			||||||
@ -437,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.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										73
									
								
								src/core/utils/signals.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/core/utils/signals.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					// (C) Copyright 2015 Moodle Pty Ltd.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    CreateEffectOptions,
 | 
				
			||||||
 | 
					    effect,
 | 
				
			||||||
 | 
					    EffectCleanupRegisterFn,
 | 
				
			||||||
 | 
					    EffectRef,
 | 
				
			||||||
 | 
					    Injector,
 | 
				
			||||||
 | 
					    model,
 | 
				
			||||||
 | 
					    ModelOptions,
 | 
				
			||||||
 | 
					    ModelSignal,
 | 
				
			||||||
 | 
					    runInInjectionContext,
 | 
				
			||||||
 | 
					} from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Return an effect wrapper that can be used to create an effect with a certain injection context.
 | 
				
			||||||
 | 
					 * Example:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 * const effectWrapper = effectWithInjectionContext(injector);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * effectWrapper(() => {
 | 
				
			||||||
 | 
					 *    // Your effect code here.
 | 
				
			||||||
 | 
					 * });
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param injector Injector to use for the effect.
 | 
				
			||||||
 | 
					 * @returns Function to create the effect.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function effectWithInjectionContext(injector: Injector): typeof effect {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        effectFn: (onCleanup: EffectCleanupRegisterFn) => void,
 | 
				
			||||||
 | 
					        options?: Omit<CreateEffectOptions, 'injector'>,
 | 
				
			||||||
 | 
					    ): EffectRef =>
 | 
				
			||||||
 | 
					        effect(effectFn, {
 | 
				
			||||||
 | 
					            ...options,
 | 
				
			||||||
 | 
					            injector,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Return a model wrapper that can be used to create a model with a certain injection context.
 | 
				
			||||||
 | 
					 * Example:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 * const modelWrapper = modelWithInjectionContext(injector);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * const myModel = modelWrapper('');
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param injector Injector to use for the model.
 | 
				
			||||||
 | 
					 * @returns Function to create the model.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function modelWithInjectionContext<T = unknown>(injector: Injector): typeof model {
 | 
				
			||||||
 | 
					    const modelFunction = (initialValue: T, opts?: ModelOptions): ModelSignal<T> =>
 | 
				
			||||||
 | 
					        runInInjectionContext(injector, () => model(initialValue, opts));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    modelFunction.required = (opts?: ModelOptions): ModelSignal<T> => runInInjectionContext(injector, () => model.required(opts));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return modelFunction as typeof model;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user