fix(forms): minor cleanup for disabled API (#63318)

* fix(forms): minor cleanup for disabled API

rename DisabledReason.reason to DisabledReason.message for consistency with errors, and allow unconditional disabling with message

* address feedback
This commit is contained in:
Miles Malerba
2025-08-21 14:48:40 -07:00
committed by GitHub
parent aa51dcdcdc
commit 6434bcce0c
6 changed files with 41 additions and 19 deletions
@@ -100,12 +100,12 @@ export class CustomValidationError extends ValidationError {
}
// @public
export function disabled<TValue, TPathKind extends PathKind = PathKind.Root>(path: FieldPath<TValue, TPathKind>, logic?: NoInfer<LogicFn<TValue, boolean | string, TPathKind>>): void;
export function disabled<TValue, TPathKind extends PathKind = PathKind.Root>(path: FieldPath<TValue, TPathKind>, logic?: string | NoInfer<LogicFn<TValue, boolean | string, TPathKind>>): void;
// @public
export interface DisabledReason {
readonly field: Field<unknown>;
readonly reason?: string;
readonly message?: string;
}
// @public
+1 -1
View File
@@ -530,7 +530,7 @@ const userSchema = schema<User>((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'}]
}
}
```
+8 -9
View File
@@ -31,23 +31,22 @@ import {addDefaultField} from './validation_errors';
*/
export function disabled<TValue, TPathKind extends PathKind = PathKind.Root>(
path: FieldPath<TValue, TPathKind>,
logic: NoInfer<LogicFn<TValue, boolean | string, TPathKind>> = () => true,
logic?: string | NoInfer<LogicFn<TValue, boolean | string, TPathKind>>,
): void {
assertPathIsCurrent(path);
const pathNode = FieldPathNode.unwrapFieldPath(path);
pathNode.logic.addDisabledReasonRule((ctx) => {
const result = logic(ctx as FieldContext<TValue, TPathKind>);
if (!result) {
return undefined;
let result: boolean | string = true;
if (typeof logic === 'string') {
result = logic;
} else if (logic) {
result = logic(ctx as FieldContext<TValue, TPathKind>);
}
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;
});
}
+2 -2
View File
@@ -72,8 +72,8 @@ export type SubmittedStatus = 'unsubmitted' | 'submitted' | 'submitting';
export interface DisabledReason {
/** The field that is disabled. */
readonly field: Field<unknown>;
/** 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. */
@@ -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!',
},
]);
});
@@ -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},
]);
});