diff --git a/goldens/public-api/forms/signals/index.api.md b/goldens/public-api/forms/signals/index.api.md index 6ba919d854e..bf06b8f5baa 100644 --- a/goldens/public-api/forms/signals/index.api.md +++ b/goldens/public-api/forms/signals/index.api.md @@ -100,12 +100,12 @@ export class CustomValidationError extends ValidationError { } // @public -export function disabled(path: FieldPath, logic?: NoInfer>): void; +export function disabled(path: FieldPath, logic?: string | NoInfer>): void; // @public export interface DisabledReason { readonly field: Field; - readonly reason?: string; + readonly message?: string; } // @public diff --git a/packages/forms/signals/docs/signal-forms.md b/packages/forms/signals/docs/signal-forms.md index 88fe91c08ef..6013736a386 100644 --- a/packages/forms/signals/docs/signal-forms.md +++ b/packages/forms/signals/docs/signal-forms.md @@ -530,7 +530,7 @@ const userSchema = schema((userPath) => { this.usersModel.set([{username: 'newuser', name: 'John Doe'}]); this.usersForm[0].username().disabled(); // true - this.usersForm[0].username().disabledReasons(); // [{field: this.usersForm[0].username, reason: 'Username cannot be changed'}] + this.usersForm[0].username().disabledReasons(); // [{field: this.usersForm[0].username, message: 'Username cannot be changed'}] } } ``` diff --git a/packages/forms/signals/src/api/logic.ts b/packages/forms/signals/src/api/logic.ts index 0eff5127c35..255386cb93d 100644 --- a/packages/forms/signals/src/api/logic.ts +++ b/packages/forms/signals/src/api/logic.ts @@ -31,23 +31,22 @@ import {addDefaultField} from './validation_errors'; */ export function disabled( path: FieldPath, - logic: NoInfer> = () => true, + logic?: string | NoInfer>, ): void { assertPathIsCurrent(path); const pathNode = FieldPathNode.unwrapFieldPath(path); pathNode.logic.addDisabledReasonRule((ctx) => { - const result = logic(ctx as FieldContext); - if (!result) { - return undefined; + let result: boolean | string = true; + if (typeof logic === 'string') { + result = logic; + } else if (logic) { + result = logic(ctx as FieldContext); } if (typeof result === 'string') { - return { - field: ctx.field, - reason: result, - }; + return {field: ctx.field, message: result}; } - return {field: ctx.field}; + return result ? {field: ctx.field} : undefined; }); } diff --git a/packages/forms/signals/src/api/types.ts b/packages/forms/signals/src/api/types.ts index 516fdfc9416..948979bed7d 100644 --- a/packages/forms/signals/src/api/types.ts +++ b/packages/forms/signals/src/api/types.ts @@ -72,8 +72,8 @@ export type SubmittedStatus = 'unsubmitted' | 'submitted' | 'submitting'; export interface DisabledReason { /** The field that is disabled. */ readonly field: Field; - /** The reason for the disablement. */ - readonly reason?: string; + /** A user-facing message describing the reason for the disablement. */ + readonly message?: string; } /** The absence of an error which indicates a successful validation result. */ diff --git a/packages/forms/signals/test/node/field_node.spec.ts b/packages/forms/signals/test/node/field_node.spec.ts index 3e4ad420368..90a205f2c93 100644 --- a/packages/forms/signals/test/node/field_node.spec.ts +++ b/packages/forms/signals/test/node/field_node.spec.ts @@ -405,7 +405,7 @@ describe('FieldNode', () => { expect(f.a().disabledReasons()).toEqual([ { field: f.a, - reason: 'a cannot be changed', + message: 'a cannot be changed', }, ]); }); @@ -428,7 +428,7 @@ describe('FieldNode', () => { expect(f.a().disabledReasons()).toEqual([ { field: f.a, - reason: 'a cannot be changed', + message: 'a cannot be changed', }, ]); }); @@ -446,14 +446,37 @@ describe('FieldNode', () => { expect(f().disabledReasons()).toEqual([ { field: f, - reason: 'form unavailable', + message: 'form unavailable', }, ]); expect(f.a().disabled()).toBe(true); expect(f.a().disabledReasons()).toEqual([ { field: f, - reason: 'form unavailable', + message: 'form unavailable', + }, + ]); + }); + + it('should disable unconditionally', () => { + const f = form( + signal({a: '', b: ''}), + (p) => { + disabled(p.a); + disabled(p.b, 'disabled!'); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f.a().disabledReasons()).toEqual([ + { + field: f.a, + }, + ]); + expect(f.b().disabledReasons()).toEqual([ + { + field: f.b, + message: 'disabled!', }, ]); }); diff --git a/packages/forms/signals/test/web/control_directive.spec.ts b/packages/forms/signals/test/web/control_directive.spec.ts index cc5ea4ecf92..d8960ee5e31 100644 --- a/packages/forms/signals/test/web/control_directive.spec.ts +++ b/packages/forms/signals/test/web/control_directive.spec.ts @@ -439,7 +439,7 @@ describe('control directive', () => { const comp = act(() => TestBed.createComponent(ReadonlyTestCmp)).componentInstance; expect(comp.myInput().disabledReasons()).toEqual([ - {reason: 'Currently unavailable', field: comp.f}, + {message: 'Currently unavailable', field: comp.f}, ]); });