diff --git a/goldens/public-api/core/index.md b/goldens/public-api/core/index.md index 1407b9370fb..7dbd111f332 100644 --- a/goldens/public-api/core/index.md +++ b/goldens/public-api/core/index.md @@ -262,6 +262,9 @@ export abstract class ComponentRef { abstract setInput(name: string, value: unknown): void; } +// @public +export function computed(computation: () => T, equal?: ValueEqualityFn): Signal; + // @public export interface ConstructorProvider extends ConstructorSansProvider { multi?: boolean; @@ -475,6 +478,16 @@ export interface DoCheck { ngDoCheck(): void; } +// @public +export interface Effect { + readonly consumer: Consumer; + destroy(): void; + schedule(): void; +} + +// @public +export function effect(effectFn: () => void): Effect; + // @public export class ElementRef { constructor(nativeElement: T); @@ -787,6 +800,9 @@ export interface InputDecorator { // @public export function isDevMode(): boolean; +// @public +export function isSignal(value: Function): value is Signal; + // @public export function isStandalone(type: Type): boolean; @@ -1300,9 +1316,24 @@ export interface SelfDecorator { new (): Self; } +// @public +export interface SettableSignal extends Signal { + mutate(mutatorFn: (value: T) => void): void; + set(value: T): void; + update(updateFn: (value: T) => T): void; +} + // @public export function setTestabilityGetter(getter: GetTestability): void; +// @public +export type Signal = (() => T) & { + [SIGNAL]: true; +}; + +// @public +export function signal(initialValue: T, equal?: ValueEqualityFn): SettableSignal; + // @public export class SimpleChange { constructor(previousValue: any, currentValue: any, firstChange: boolean); @@ -1421,6 +1452,12 @@ export interface TypeDecorator { export interface TypeProvider extends Type { } +// @public +export function untracked(nonReactiveReadsFn: () => T): T; + +// @public +export type ValueEqualityFn = (a: T, b: T) => boolean; + // @public export interface ValueProvider extends ValueSansProvider { multi?: boolean; diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index ac1fe45cef8..63a514e3beb 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -34,6 +34,7 @@ export {EventEmitter} from './event_emitter'; export {ErrorHandler} from './error_handler'; export * from './core_private_export'; export * from './core_render3_private_export'; +export * from './core_reactivity_export'; export {SecurityContext} from './sanitization/security'; export {Sanitizer} from './sanitization/sanitizer'; export {createNgModule, createNgModuleRef, createEnvironmentInjector} from './render3/ng_module_ref'; diff --git a/packages/core/src/core_reactivity_export.ts b/packages/core/src/core_reactivity_export.ts new file mode 100644 index 00000000000..d92bbf77edb --- /dev/null +++ b/packages/core/src/core_reactivity_export.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// This file exists to allow the set of reactivity exports to be modified in g3, as these APIs are +// only "beta" for the time being. + +export * from './core_reactivity_export_internal'; diff --git a/packages/core/src/core_reactivity_export_internal.ts b/packages/core/src/core_reactivity_export_internal.ts new file mode 100644 index 00000000000..b617e178a16 --- /dev/null +++ b/packages/core/src/core_reactivity_export_internal.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export {computed, effect, Effect, isSignal, SettableSignal, Signal, signal, untracked, ValueEqualityFn} from './signals'; diff --git a/packages/core/src/signals/index.ts b/packages/core/src/signals/index.ts index 10247bfe699..0bce60ccd75 100644 --- a/packages/core/src/signals/index.ts +++ b/packages/core/src/signals/index.ts @@ -8,8 +8,8 @@ export {isSignal, Signal, ValueEqualityFn} from './src/api'; export {computed} from './src/computed'; -export {effect} from './src/effect'; +export {effect, Effect} from './src/effect'; export {setActiveConsumer} from './src/graph'; export {SettableSignal, signal} from './src/signal'; -export {untracked as untrack} from './src/untracked'; +export {untracked} from './src/untracked'; export {Watch} from './src/watch'; diff --git a/packages/core/src/signals/src/effect.ts b/packages/core/src/signals/src/effect.ts index 5bf1f396d8c..20bc418d252 100644 --- a/packages/core/src/signals/src/effect.ts +++ b/packages/core/src/signals/src/effect.ts @@ -11,6 +11,8 @@ import {Watch} from './watch'; /** * A global reactive effect, which can be manually scheduled or destroyed. + * + * @developerPreview */ export interface Effect { /** diff --git a/packages/core/test/signals/non_reactive_spec.ts b/packages/core/test/signals/non_reactive_spec.ts index 51d5515fe54..7614afa60af 100644 --- a/packages/core/test/signals/non_reactive_spec.ts +++ b/packages/core/test/signals/non_reactive_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {computed, signal, untrack} from '@angular/core/src/signals'; +import {computed, signal, untracked} from '@angular/core/src/signals'; import {effect, effectsDone as flush, resetEffects} from '@angular/core/src/signals/src/effect'; describe('non-reactive reads', () => { @@ -17,15 +17,15 @@ describe('non-reactive reads', () => { it('should read the latest value from signal', () => { const counter = signal(0); - expect(untrack(counter)).toEqual(0); + expect(untracked(counter)).toEqual(0); counter.set(1); - expect(untrack(counter)).toEqual(1); + expect(untracked(counter)).toEqual(1); }); it('should not add dependencies to computed when reading a value from a signal', () => { const counter = signal(0); - const double = computed(() => untrack(counter) * 2); + const double = computed(() => untracked(counter) * 2); expect(double()).toEqual(0); @@ -37,10 +37,10 @@ describe('non-reactive reads', () => { const counter = signal(0); const double = computed(() => counter() * 2); - expect(untrack(double)).toEqual(0); + expect(untracked(double)).toEqual(0); counter.set(2); - expect(untrack(double)).toEqual(4); + expect(untracked(double)).toEqual(4); }); it('should not make surrounding effect depend on the signal', async () => { @@ -48,7 +48,7 @@ describe('non-reactive reads', () => { const runLog: number[] = []; effect(() => { - runLog.push(untrack(s)); + runLog.push(untracked(s)); }); // an effect will run at least once @@ -89,7 +89,7 @@ describe('non-reactive reads', () => { let runLog: string[] = []; const effectRef = effect(() => { - untrack(() => runLog.push(`${first()} ${last()}`)); + untracked(() => runLog.push(`${first()} ${last()}`)); }); // effects run at least once