Skip to content

Commit 6b66f4b

Browse files
feat(core): signal utils for input and model
provides `isInputSignal` and `isModelSignal` to check whether given value is InputSignal or ModelSignal respectively.
1 parent a7f20eb commit 6b66f4b

File tree

5 files changed

+53
-3
lines changed

5 files changed

+53
-3
lines changed

packages/core/src/authoring/input/input_signal_node.ts

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const INPUT_SIGNAL_NODE: InputSignalNode<unknown, unknown> = /* @__PURE__
4444
return {
4545
...SIGNAL_NODE,
4646
transformFn: undefined,
47+
kind: 'input',
4748

4849
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T) {
4950
signalSetFn(node, value);

packages/core/src/core_reactivity_export_internal.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88

99
export {SIGNAL as ɵSIGNAL} from '@angular/core/primitives/signals';
1010

11-
export {isSignal, Signal, ValueEqualityFn} from './render3/reactivity/api';
11+
export {
12+
isSignal,
13+
isInputSignal,
14+
isModelSignal,
15+
Signal,
16+
ValueEqualityFn,
17+
} from './render3/reactivity/api';
1218
export {computed, CreateComputedOptions} from './render3/reactivity/computed';
1319
export {
1420
CreateSignalOptions,

packages/core/src/render3/reactivity/api.ts

+15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {SIGNAL} from '@angular/core/primitives/signals';
10+
import {InputSignal, ModelSignal} from '../../authoring';
1011

1112
/**
1213
* A reactive value which notifies consumers of any changes.
@@ -27,6 +28,20 @@ export function isSignal(value: unknown): value is Signal<unknown> {
2728
return typeof value === 'function' && (value as Signal<unknown>)[SIGNAL] !== undefined;
2829
}
2930

31+
/**
32+
* Checks if the given `value` is a reactive `InputSignal`.
33+
*/
34+
export function isInputSignal(value: unknown): value is InputSignal<unknown> {
35+
return isSignal(value) && (value as InputSignal<unknown>)[SIGNAL].kind === 'input';
36+
}
37+
38+
/**
39+
* Checks if the given `value` is a reactive `ModelSignal`.
40+
*/
41+
export function isModelSignal(value: unknown): value is ModelSignal<unknown> {
42+
return isInputSignal(value) && typeof (value as ModelSignal<unknown>).subscribe === 'function';
43+
}
44+
3045
/**
3146
* A comparison function which can determine if two values are equal.
3247
*/

packages/core/test/signals/BUILD.bazel

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ ts_library(
1212
"//packages/core",
1313
"//packages/core/primitives/signals",
1414
"//packages/core/src/util",
15+
"//packages/core/testing",
1516
],
1617
)
1718

1819
jasmine_node_test(
1920
name = "signals",
20-
bootstrap = ["//tools/testing:node_no_angular"],
21+
bootstrap = ["//tools/testing:node"],
2122
deps = [
2223
":signals_lib",
2324
],

packages/core/test/signals/is_signal_spec.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {computed, isSignal, signal} from '@angular/core';
9+
import {computed, input, isSignal, model, runInInjectionContext, signal} from '@angular/core';
10+
import {createInputSignal} from '@angular/core/src/authoring/input/input_signal';
11+
import {isInputSignal, isModelSignal} from '@angular/core/src/render3/reactivity/api';
12+
import {TestBed} from '@angular/core/testing';
1013

1114
describe('isSignal', () => {
1215
it('should return true for writable signal', () => {
@@ -34,3 +37,27 @@ describe('isSignal', () => {
3437
expect(isSignal(fn)).toBe(false);
3538
});
3639
});
40+
41+
describe('isInputSignal', () => {
42+
it('should return true for input signal', () => {
43+
const inputSignal = TestBed.runInInjectionContext(() => input('Angular'));
44+
expect(isInputSignal(inputSignal)).toBe(true);
45+
});
46+
47+
it('should return false for signal', () => {
48+
const writableSignal = signal('Angular');
49+
expect(isInputSignal(writableSignal)).toBe(false);
50+
});
51+
});
52+
53+
describe('isModelSignal', () => {
54+
it('should return true for model signal', () => {
55+
const modelSignal = TestBed.runInInjectionContext(() => model('Angular'));
56+
expect(isModelSignal(modelSignal)).toBe(true);
57+
});
58+
59+
it('should return false for input signal', () => {
60+
const inputSignal = createInputSignal('Angular');
61+
expect(isModelSignal(inputSignal)).toBe(false);
62+
});
63+
});

0 commit comments

Comments
 (0)