Skip to content

Commit 7243c70

Browse files
JeanMechepkozlowski-opensource
authored andcommitted
fix(core): return a readonly signal on asReadonly. (#54706)
Previous `asReadonly()` returned the signal value and not the signal itself. Fixes #54704 PR Close #54706
1 parent e5885fa commit 7243c70

3 files changed

Lines changed: 14 additions & 4 deletions

File tree

packages/core/src/authoring/model/model_signal.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {producerAccessed, SIGNAL, signalSetFn} from '@angular/core/primitives/si
1010

1111
import {RuntimeError, RuntimeErrorCode} from '../../errors';
1212
import {Signal} from '../../render3/reactivity/api';
13-
import {WritableSignal, ɵWRITABLE_SIGNAL} from '../../render3/reactivity/signal';
13+
import {signalAsReadonlyFn, WritableSignal, ɵWRITABLE_SIGNAL} from '../../render3/reactivity/signal';
1414
import {ɵINPUT_SIGNAL_BRAND_READ_TYPE, ɵINPUT_SIGNAL_BRAND_WRITE_TYPE} from '../input/input_signal';
1515
import {INPUT_SIGNAL_NODE, InputSignalNode, REQUIRED_UNSET_VALUE} from '../input/input_signal_node';
1616
import {OutputEmitterRef} from '../output/output_emitter_ref';
@@ -63,7 +63,7 @@ export function createModelSignal<T>(initialValue: T): ModelSignal<T> {
6363
}
6464

6565
getter[SIGNAL] = node;
66-
getter.asReadonly = (() => getter()) as () => Signal<T>;
66+
getter.asReadonly = signalAsReadonlyFn.bind(getter as any) as () => Signal<T>;
6767

6868
// TODO: Should we throw an error when updating a destroyed model?
6969
getter.set = (newValue: T) => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function signal<T>(initialValue: T, options?: CreateSignalOptions<T>): Wr
8080
return signalFn as WritableSignal<T>;
8181
}
8282

83-
function signalAsReadonlyFn<T>(this: SignalGetter<T>): Signal<T> {
83+
export function signalAsReadonlyFn<T>(this: SignalGetter<T>): Signal<T> {
8484
const node = this[SIGNAL] as SignalNode<T>& {readonlyFn?: Signal<T>};
8585
if (node.readonlyFn === undefined) {
8686
const readonlyFn = () => this();

packages/core/test/authoring/model_input_spec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {computed, model} from '@angular/core';
9+
import {computed, isSignal, model, WritableSignal} from '@angular/core';
1010
import {TestBed} from '@angular/core/testing';
1111

1212
describe('model signal', () => {
@@ -35,6 +35,16 @@ describe('model signal', () => {
3535
expect(signal()).toBe(18);
3636
});
3737

38+
it('should return readonly signal', () => {
39+
const signal = TestBed.runInInjectionContext(() => model(2));
40+
const readOnly = signal.asReadonly();
41+
42+
expect(isSignal(readOnly)).toBeTrue();
43+
expect(readOnly()).toBe(2);
44+
expect((readOnly as WritableSignal<unknown>).set).toBeUndefined();
45+
expect((readOnly as WritableSignal<unknown>).update).toBeUndefined();
46+
});
47+
3848
it('should emit when the value changes', () => {
3949
const signal = TestBed.runInInjectionContext(() => model(1));
4050
const values: number[] = [];

0 commit comments

Comments
 (0)