From 9f910d338fd9f6826dcd550a778c96e41061b192 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 31 Jul 2024 09:56:42 +0200 Subject: [PATCH] MOBILE-4612 compile: Expose model and untracked to plugins --- src/core/features/compile/services/compile.ts | 19 ++--- src/core/utils/signals.ts | 73 +++++++++++++++++++ 2 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 src/core/utils/signals.ts diff --git a/src/core/features/compile/services/compile.ts b/src/core/features/compile/services/compile.ts index 476c60c0d..b22e45494 100644 --- a/src/core/features/compile/services/compile.ts +++ b/src/core/features/compile/services/compile.ts @@ -23,10 +23,7 @@ import { ViewContainerRef, signal, computed, - effect, - EffectCleanupRegisterFn, - CreateEffectOptions, - EffectRef, + untracked, } from '@angular/core'; import { ActionSheetController, @@ -41,6 +38,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreLogger } from '@singletons/logger'; import { CoreEvents } from '@singletons/events'; import { makeSingleton } from '@singletons'; +import { effectWithInjectionContext, modelWithInjectionContext } from '@/core/utils/signals'; // Import core services. import { getCoreServices } from '@/core/core.module'; @@ -306,15 +304,10 @@ export class CoreCompileProvider { instance['Md5'] = Md5; instance['signal'] = signal; instance['computed'] = computed; - // Create a wrapper to call effect with the proper injection context. - instance['effect'] = ( - effectFn: (onCleanup: EffectCleanupRegisterFn) => void, - options?: Omit, - ): EffectRef => - effect(effectFn, { - ...options, - injector, - }); + instance['untracked'] = untracked; + instance['effect'] = effectWithInjectionContext(injector); + instance['model'] = modelWithInjectionContext(injector); + /** * @deprecated since 4.1, plugins should use CoreNetwork instead. * Keeping this a bit more to avoid plugins breaking. diff --git a/src/core/utils/signals.ts b/src/core/utils/signals.ts new file mode 100644 index 000000000..259e25879 --- /dev/null +++ b/src/core/utils/signals.ts @@ -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, + ): 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(injector: Injector): typeof model { + const modelFunction = (initialValue: T, opts?: ModelOptions): ModelSignal => + runInInjectionContext(injector, () => model(initialValue, opts)); + + modelFunction.required = (opts?: ModelOptions): ModelSignal => runInInjectionContext(injector, () => model.required(opts)); + + return modelFunction as typeof model; +}