mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
feat(core): add Angular Signals to the public API (#49150)
This commit exposes `signal`, `computed`, `effect` and various helpers from the `@angular/core` entrypoint. These APIs are marked as `@developerPreview` and are still prototypes in active development. Their final shapes will be subject to our internal design reviews as well as one or more community RFCs. We're exporting them now to allow for experimentation using 16.0.0 next and RC releases. PR Close #49150
This commit is contained in:
parent
5dce2a5a3a
commit
bc5ddabdcb
7 changed files with 71 additions and 10 deletions
|
|
@ -262,6 +262,9 @@ export abstract class ComponentRef<C> {
|
|||
abstract setInput(name: string, value: unknown): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function computed<T>(computation: () => T, equal?: ValueEqualityFn<T>): Signal<T>;
|
||||
|
||||
// @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<T = any> {
|
||||
constructor(nativeElement: T);
|
||||
|
|
@ -787,6 +800,9 @@ export interface InputDecorator {
|
|||
// @public
|
||||
export function isDevMode(): boolean;
|
||||
|
||||
// @public
|
||||
export function isSignal(value: Function): value is Signal<unknown>;
|
||||
|
||||
// @public
|
||||
export function isStandalone(type: Type<unknown>): boolean;
|
||||
|
||||
|
|
@ -1300,9 +1316,24 @@ export interface SelfDecorator {
|
|||
new (): Self;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface SettableSignal<T> extends Signal<T> {
|
||||
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> = (() => T) & {
|
||||
[SIGNAL]: true;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function signal<T>(initialValue: T, equal?: ValueEqualityFn<T>): SettableSignal<T>;
|
||||
|
||||
// @public
|
||||
export class SimpleChange {
|
||||
constructor(previousValue: any, currentValue: any, firstChange: boolean);
|
||||
|
|
@ -1421,6 +1452,12 @@ export interface TypeDecorator {
|
|||
export interface TypeProvider extends Type<any> {
|
||||
}
|
||||
|
||||
// @public
|
||||
export function untracked<T>(nonReactiveReadsFn: () => T): T;
|
||||
|
||||
// @public
|
||||
export type ValueEqualityFn<T> = (a: T, b: T) => boolean;
|
||||
|
||||
// @public
|
||||
export interface ValueProvider extends ValueSansProvider {
|
||||
multi?: boolean;
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
12
packages/core/src/core_reactivity_export.ts
Normal file
12
packages/core/src/core_reactivity_export.ts
Normal file
|
|
@ -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';
|
||||
9
packages/core/src/core_reactivity_export_internal.ts
Normal file
9
packages/core/src/core_reactivity_export_internal.ts
Normal file
|
|
@ -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';
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import {Watch} from './watch';
|
|||
|
||||
/**
|
||||
* A global reactive effect, which can be manually scheduled or destroyed.
|
||||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
export interface Effect {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue