diff --git a/packages/forms/signals/src/api/rules/disabled.ts b/packages/forms/signals/src/api/rules/disabled.ts index e39de7558b4..3436614556d 100644 --- a/packages/forms/signals/src/api/rules/disabled.ts +++ b/packages/forms/signals/src/api/rules/disabled.ts @@ -15,8 +15,9 @@ import type {FieldContext, LogicFn, PathKind, SchemaPath, SchemaPathRules} from * validation, touched/dirty, or other state of its parent field. * * @param path The target path to add the disabled logic to. - * @param logic A reactive function that returns `true` (or a string reason) when the field is disabled, - * and `false` when it is not disabled. + * @param config Optional configuration object. + * - `when`: A reactive function that returns `true` (or a string reason) when the field is disabled, + * and `false` when it is not disabled. Can also be a static string reason. * @template TValue The type of value stored in the field the logic is bound to. * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) * @@ -25,17 +26,17 @@ import type {FieldContext, LogicFn, PathKind, SchemaPath, SchemaPathRules} from */ export function disabled( path: SchemaPath, - logic?: string | NoInfer>, + config?: {when?: string | NoInfer>}, ): void { assertPathIsCurrent(path); const pathNode = FieldPathNode.unwrapFieldPath(path); pathNode.builder.addDisabledReasonRule((ctx) => { let result: boolean | string = true; - if (typeof logic === 'string') { - result = logic; - } else if (logic) { - result = logic(ctx as FieldContext); + if (typeof config?.when === 'string') { + result = config.when; + } else if (config?.when) { + result = config.when(ctx as FieldContext); } if (typeof result === 'string') { return {fieldTree: ctx.fieldTree, message: result}; diff --git a/packages/forms/signals/src/api/rules/hidden.ts b/packages/forms/signals/src/api/rules/hidden.ts index 1351731acb9..ee1069c1224 100644 --- a/packages/forms/signals/src/api/rules/hidden.ts +++ b/packages/forms/signals/src/api/rules/hidden.ts @@ -23,7 +23,8 @@ import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; * ``` * * @param path The target path to add the hidden logic to. - * @param logic A reactive function that returns `true` when the field is hidden. + * @param config Options object containing the `when` condition. + * - `when`: A reactive function that returns `true` when the field is hidden. * @template TValue The type of value stored in the field the logic is bound to. * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) * @@ -32,10 +33,10 @@ import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; */ export function hidden( path: SchemaPath, - logic: NoInfer>, + config: {when: NoInfer>}, ): void { assertPathIsCurrent(path); const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addHiddenRule(logic); + pathNode.builder.addHiddenRule(config.when); } diff --git a/packages/forms/signals/src/api/rules/readonly.ts b/packages/forms/signals/src/api/rules/readonly.ts index b3dc8ff9fea..fb9a4281c76 100644 --- a/packages/forms/signals/src/api/rules/readonly.ts +++ b/packages/forms/signals/src/api/rules/readonly.ts @@ -15,7 +15,8 @@ import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; * the validation, touched/dirty, or other state of its parent field. * * @param path The target path to make readonly. - * @param logic A reactive function that returns `true` when the field is readonly. + * @param config Optional configuration object. + * - `when`: A reactive function that returns `true` when the field is readonly. * @template TValue The type of value stored in the field the logic is bound to. * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) * @@ -24,10 +25,10 @@ import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; */ export function readonly( path: SchemaPath, - logic: NoInfer> = () => true, + config?: {when?: NoInfer>}, ) { assertPathIsCurrent(path); const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addReadonlyRule(logic); + pathNode.builder.addReadonlyRule(config?.when ?? (() => true)); } diff --git a/packages/forms/signals/src/api/rules/validation/email.ts b/packages/forms/signals/src/api/rules/validation/email.ts index e19cd39d842..b335e475cb1 100644 --- a/packages/forms/signals/src/api/rules/validation/email.ts +++ b/packages/forms/signals/src/api/rules/validation/email.ts @@ -63,6 +63,9 @@ export function email( config?: BaseValidatorConfig, ) { validate(path, (ctx) => { + if (config?.when && !config.when(ctx)) { + return undefined; + } if (isEmpty(ctx.value())) { return undefined; } diff --git a/packages/forms/signals/src/api/rules/validation/max.ts b/packages/forms/signals/src/api/rules/validation/max.ts index 20a2dfefa0f..a65064b71ad 100644 --- a/packages/forms/signals/src/api/rules/validation/max.ts +++ b/packages/forms/signals/src/api/rules/validation/max.ts @@ -38,15 +38,18 @@ export function max(); // Memoize the maximum valid value. - metadata(path, MAX_MEMO, (ctx) => (typeof maxValue === 'function' ? maxValue(ctx) : maxValue)); + metadata(path, MAX_MEMO, (ctx) => { + if (config?.when && !config.when(ctx)) { + return undefined; + } + return typeof maxValue === 'function' ? maxValue(ctx) : maxValue; + }); // Publish the memoized maximum value for aggregation. metadata(path, MAX_NUMBER, ({state}) => state.metadata(MAX_MEMO)!()); // Use `MAX_NUMBER` to define the `max` property of the field. metadata(path, MAX, () => MAX_NUMBER as LimitKey); - - // Validate that the field value is not greater than the maximum value. validate(path, (ctx) => { const value = ctx.value(); if (value === null || Number.isNaN(value)) { diff --git a/packages/forms/signals/src/api/rules/validation/max_length.ts b/packages/forms/signals/src/api/rules/validation/max_length.ts index e65e6ad88de..9514961ac2d 100644 --- a/packages/forms/signals/src/api/rules/validation/max_length.ts +++ b/packages/forms/signals/src/api/rules/validation/max_length.ts @@ -44,9 +44,12 @@ export function maxLength< maxLength: number | LogicFn, config?: BaseValidatorConfig, ) { - const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), (ctx) => - typeof maxLength === 'number' ? maxLength : maxLength(ctx), - ); + const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), (ctx) => { + if (config?.when && !config.when(ctx)) { + return undefined; + } + return typeof maxLength === 'number' ? maxLength : maxLength(ctx); + }); metadata(path, MAX_LENGTH, ({state}) => state.metadata(MAX_LENGTH_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { diff --git a/packages/forms/signals/src/api/rules/validation/min.ts b/packages/forms/signals/src/api/rules/validation/min.ts index 3cff4d364d6..ef7e9ea6fb4 100644 --- a/packages/forms/signals/src/api/rules/validation/min.ts +++ b/packages/forms/signals/src/api/rules/validation/min.ts @@ -38,15 +38,18 @@ export function min(); // Memomize the minimum valid. - metadata(path, MIN_MEMO, (ctx) => (typeof minValue === 'function' ? minValue(ctx) : minValue)); + metadata(path, MIN_MEMO, (ctx) => { + if (config?.when && !config.when(ctx)) { + return undefined; + } + return typeof minValue === 'function' ? minValue(ctx) : minValue; + }); // Publish the memoized mininum value for aggregation. metadata(path, MIN_NUMBER, ({state}) => state.metadata(MIN_MEMO)!()); // Use `MIN_NUMBER` to define the `min` property of the field. metadata(path, MIN, () => MIN_NUMBER as LimitKey); - - // Validate that the field value is not less than the minimum value. validate(path, (ctx) => { const value = ctx.value(); if (value === null || Number.isNaN(value)) { diff --git a/packages/forms/signals/src/api/rules/validation/min_length.ts b/packages/forms/signals/src/api/rules/validation/min_length.ts index a60baf4fed4..8aaa83bf864 100644 --- a/packages/forms/signals/src/api/rules/validation/min_length.ts +++ b/packages/forms/signals/src/api/rules/validation/min_length.ts @@ -44,9 +44,12 @@ export function minLength< minLength: number | LogicFn, config?: BaseValidatorConfig, ) { - const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), (ctx) => - typeof minLength === 'number' ? minLength : minLength(ctx), - ); + const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), (ctx) => { + if (config?.when && !config.when(ctx)) { + return undefined; + } + return typeof minLength === 'number' ? minLength : minLength(ctx); + }); metadata(path, MIN_LENGTH, ({state}) => state.metadata(MIN_LENGTH_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { diff --git a/packages/forms/signals/src/api/rules/validation/pattern.ts b/packages/forms/signals/src/api/rules/validation/pattern.ts index fa695440dd2..c74acf65e9f 100644 --- a/packages/forms/signals/src/api/rules/validation/pattern.ts +++ b/packages/forms/signals/src/api/rules/validation/pattern.ts @@ -33,9 +33,12 @@ export function pattern( pattern: RegExp | LogicFn, config?: BaseValidatorConfig, ) { - const PATTERN_MEMO = metadata(path, createMetadataKey(), (ctx) => - pattern instanceof RegExp ? pattern : pattern(ctx), - ); + const PATTERN_MEMO = metadata(path, createMetadataKey(), (ctx) => { + if (config?.when && !config.when(ctx)) { + return undefined; + } + return pattern instanceof RegExp ? pattern : pattern(ctx); + }); metadata(path, PATTERN, ({state}) => state.metadata(PATTERN_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { diff --git a/packages/forms/signals/src/api/rules/validation/util.ts b/packages/forms/signals/src/api/rules/validation/util.ts index 0befeed29f8..56ff6784290 100644 --- a/packages/forms/signals/src/api/rules/validation/util.ts +++ b/packages/forms/signals/src/api/rules/validation/util.ts @@ -18,6 +18,8 @@ export type BaseValidatorConfig; error?: never; + /** A function that receives the `FieldContext` and returns true if the validator should be applied. */ + when?: NoInfer>; } | { /** @@ -26,6 +28,8 @@ export type BaseValidatorConfig | LogicFn, TPathKind>; message?: never; + /** A function that receives the `FieldContext` and returns true if the validator should be applied. */ + when?: NoInfer>; }; /** Gets the length or size of the given value. */ diff --git a/packages/forms/signals/src/api/rules/validation/validate_async.ts b/packages/forms/signals/src/api/rules/validation/validate_async.ts index 04a67767dbf..9278da0a721 100644 --- a/packages/forms/signals/src/api/rules/validation/validate_async.ts +++ b/packages/forms/signals/src/api/rules/validation/validate_async.ts @@ -21,6 +21,7 @@ import {FieldPathNode} from '../../../schema/path_node'; import {assertPathIsCurrent} from '../../../schema/schema'; import { FieldContext, + LogicFn, PathKind, SchemaPath, SchemaPathRules, @@ -107,6 +108,10 @@ export interface AsyncValidatorOptions< * If a field is not given, the error is assumed to apply to the field being validated. */ readonly onSuccess: MapToErrorsFn; + /** + * A function that receives the field context and returns true if the async validation should be run. + */ + readonly when?: NoInfer>; } /** @@ -149,6 +154,9 @@ export function validateAsync; + /** + * A function that receives the field context and returns true if the async validation should be run. + */ + readonly when?: NoInfer>; } /** @@ -96,5 +101,6 @@ export function validateHttp) => httpResource(request, opts.options), onSuccess: opts.onSuccess, onError: opts.onError, + when: opts.when, }); } diff --git a/packages/forms/signals/test/node/api/hidden.spec.ts b/packages/forms/signals/test/node/api/hidden.spec.ts index b21438f71bc..5d1f6fdd73f 100644 --- a/packages/forms/signals/test/node/api/hidden.spec.ts +++ b/packages/forms/signals/test/node/api/hidden.spec.ts @@ -16,8 +16,10 @@ describe('hidden', () => { const f = form( cat, (p) => { - hidden(p, ({value}) => { - return value.name === 'hidden-cat'; + hidden(p, { + when: ({value}) => { + return value.name === 'hidden-cat'; + }, }); }, {injector: TestBed.inject(Injector)}, @@ -32,8 +34,10 @@ describe('hidden', () => { const f = form( cat, (p) => { - hidden(p.name, ({value}) => { - return value() === 'hidden-cat'; + hidden(p.name, { + when: ({value}) => { + return value() === 'hidden-cat'; + }, }); }, {injector: TestBed.inject(Injector)}, @@ -48,8 +52,10 @@ describe('hidden', () => { const f = form( cat, (p) => { - hidden(p, ({value}) => { - return value().name === 'hidden-cat'; + hidden(p, { + when: ({value}) => { + return value().name === 'hidden-cat'; + }, }); }, {injector: TestBed.inject(Injector)}, @@ -65,8 +71,10 @@ describe('hidden', () => { const f = form( cat, (p) => { - hidden(p.name, ({value}) => { - return value() === 'hidden-cat'; + hidden(p.name, { + when: ({value}) => { + return value() === 'hidden-cat'; + }, }); validate(p.name, () => { @@ -94,8 +102,10 @@ describe('hidden', () => { const f = form( cat, (p) => { - hidden(p.name, ({value}) => { - return value() === 'hidden-cat'; + hidden(p.name, { + when: ({value}) => { + return value() === 'hidden-cat'; + }, }); }, {injector: TestBed.inject(Injector)}, diff --git a/packages/forms/signals/test/node/compat/compat.spec.ts b/packages/forms/signals/test/node/compat/compat.spec.ts index f925e9cd01d..781d2050ad3 100644 --- a/packages/forms/signals/test/node/compat/compat.spec.ts +++ b/packages/forms/signals/test/node/compat/compat.spec.ts @@ -254,8 +254,10 @@ describe('Forms compat', () => { const f = compatForm( cat, (p) => { - disabled(p, ({value}) => { - return value().name === 'disabled-cat'; + disabled(p, { + when: ({value}) => { + return value().name === 'disabled-cat'; + }, }); }, { @@ -286,8 +288,10 @@ describe('Forms compat', () => { const f = compatForm( cat, (p) => { - hidden(p, ({value}) => { - return value().name === 'hidden-cat'; + hidden(p, { + when: ({value}) => { + return value().name === 'hidden-cat'; + }, }); }, { @@ -717,8 +721,10 @@ describe('Forms compat', () => { return valueOf(path.age) < 8 ? [] : []; }); - readonly(path.name, ({valueOf}) => { - return valueOf(path.age) < 8; + readonly(path.name, { + when: ({valueOf}) => { + return valueOf(path.age) < 8; + }, }); email(path.name, { diff --git a/packages/forms/signals/test/node/compat/extract_value.spec.ts b/packages/forms/signals/test/node/compat/extract_value.spec.ts index 3590e032e95..b0688df48a8 100644 --- a/packages/forms/signals/test/node/compat/extract_value.spec.ts +++ b/packages/forms/signals/test/node/compat/extract_value.spec.ts @@ -227,7 +227,7 @@ describe('extractValue', () => { signal(model), (p) => { applyEach(p.items, (item) => { - disabled(item, ({value}) => value() === 2 || value() === 4); + disabled(item, {when: ({value}) => value() === 2 || value() === 4}); }); }, {injector}, @@ -242,7 +242,7 @@ describe('extractValue', () => { signal(model), (p) => { applyEach(p.items, (item) => { - disabled(item, ({value}) => value() !== 30); + disabled(item, {when: ({value}) => value() !== 30}); }); }, {injector}, diff --git a/packages/forms/signals/test/node/compat/signal_form_control.spec.ts b/packages/forms/signals/test/node/compat/signal_form_control.spec.ts index e8db25c1ffe..3e3c98fb13a 100644 --- a/packages/forms/signals/test/node/compat/signal_form_control.spec.ts +++ b/packages/forms/signals/test/node/compat/signal_form_control.spec.ts @@ -140,7 +140,7 @@ describe('SignalFormControl', () => { it('should support disabled via rules', () => { const form = createSignalFormControl(10, (p) => { - disabled(p, ({value}) => value() > 15); + disabled(p, {when: ({value}) => value() > 15}); }); expect(form.disabled).toBe(false); @@ -656,7 +656,7 @@ describe('SignalFormControl', () => { describe('callback registration', () => { it('should call registered onDisabledChange callback when disabled state changes', () => { const form = createSignalFormControl(10, (p) => { - disabled(p, ({value}) => value() > 15); + disabled(p, {when: ({value}) => value() > 15}); }); const callback = jasmine.createSpy('onDisabledChange'); @@ -674,7 +674,7 @@ describe('SignalFormControl', () => { it('should NOT track signals read inside onDisabledChange callback', () => { const form = createSignalFormControl(10, (p) => { - disabled(p, ({value}) => value() > 15); + disabled(p, {when: ({value}) => value() > 15}); }); const otherSignal = signal(0); const callback = jasmine.createSpy('onDisabledChange').and.callFake(() => { diff --git a/packages/forms/signals/test/node/field_node.spec.ts b/packages/forms/signals/test/node/field_node.spec.ts index af1467f321f..a630a7b99f5 100644 --- a/packages/forms/signals/test/node/field_node.spec.ts +++ b/packages/forms/signals/test/node/field_node.spec.ts @@ -288,7 +288,7 @@ describe('FieldNode', () => { b: 2, }), (p) => { - hidden(p, () => true); + hidden(p, {when: () => true}); }, {injector: TestBed.inject(Injector)}, ); @@ -353,7 +353,7 @@ describe('FieldNode', () => { b: 2, }), (p) => { - readonly(p, isReadonly); + readonly(p, {when: isReadonly}); }, {injector: TestBed.inject(Injector)}, ); @@ -537,7 +537,7 @@ describe('FieldNode', () => { b: 2, }), (p) => { - hidden(p, () => true); + hidden(p, {when: () => true}); }, {injector: TestBed.inject(Injector)}, ); @@ -602,7 +602,7 @@ describe('FieldNode', () => { b: 2, }), (p) => { - hidden(p, isHidden); + hidden(p, {when: isHidden}); }, {injector: TestBed.inject(Injector)}, ); @@ -669,11 +669,13 @@ describe('FieldNode', () => { signal({names: [{name: 'Alex'}, {name: 'Miles'}]}), (p) => { applyEach(p.names, (a) => { - disabled(a.name, ({value, fieldTreeOf}) => { - const el = fieldTreeOf(a); - expect(el().value().name).toBe(value()); - expect([...fieldTreeOf(p).names].findIndex((e: any) => e === el)).not.toBe(-1); - return true; + disabled(a.name, { + when: ({value, fieldTreeOf}) => { + const el = fieldTreeOf(a); + expect(el().value().name).toBe(value()); + expect([...fieldTreeOf(p).names].findIndex((e: any) => e === el)).not.toBe(-1); + return true; + }, }); }); }, @@ -689,7 +691,7 @@ describe('FieldNode', () => { (p) => { applyEach(p, (a) => { a; - disabled(a, ({value}) => value() % 2 === 0); + disabled(a, {when: ({value}) => value() % 2 === 0}); }); }, {injector: TestBed.inject(Injector)}, @@ -706,7 +708,7 @@ describe('FieldNode', () => { (p) => { applyEach(p, (el) => { // Disabled if even. - disabled(el, ({value}) => value() % 2 === 0); + disabled(el, {when: ({value}) => value() % 2 === 0}); }); }, {injector: TestBed.inject(Injector)}, @@ -802,7 +804,7 @@ describe('FieldNode', () => { const f = form( signal({a: 1, b: 2}), (p) => { - disabled(p.a, ({value}) => value() !== 2); + disabled(p.a, {when: ({value}) => value() !== 2}); }, {injector: TestBed.inject(Injector)}, ); @@ -820,7 +822,7 @@ describe('FieldNode', () => { const f = form( signal({a: 1, b: 2}), (p) => { - disabled(p.a, () => 'a cannot be changed'); + disabled(p.a, {when: () => 'a cannot be changed'}); }, {injector: TestBed.inject(Injector)}, ); @@ -838,7 +840,7 @@ describe('FieldNode', () => { const f = form( signal({a: 1, b: 2}), (p) => { - disabled(p.a, ({value}) => (value() > 5 ? 'a cannot be changed' : false)); + disabled(p.a, {when: ({value}) => (value() > 5 ? 'a cannot be changed' : false)}); }, {injector: TestBed.inject(Injector)}, ); @@ -861,7 +863,7 @@ describe('FieldNode', () => { const f = form( signal({a: 1, b: 2}), (p) => { - disabled(p, () => 'form unavailable'); + disabled(p, {when: () => 'form unavailable'}); }, {injector: TestBed.inject(Injector)}, ); @@ -887,7 +889,7 @@ describe('FieldNode', () => { signal({a: '', b: ''}), (p) => { disabled(p.a); - disabled(p.b, 'disabled!'); + disabled(p.b, {when: 'disabled!'}); }, {injector: TestBed.inject(Injector)}, ); @@ -925,7 +927,7 @@ describe('FieldNode', () => { const f = form( signal({a: 1, b: 2}), (p) => { - readonly(p.a, ({value}) => value() > 10); + readonly(p.a, {when: ({value}) => value() > 10}); }, {injector: TestBed.inject(Injector)}, ); @@ -955,7 +957,7 @@ describe('FieldNode', () => { const f = form( signal(''), (p) => { - readonly(p, isReadonly); + readonly(p, {when: isReadonly}); required(p); }, {injector: TestBed.inject(Injector)}, @@ -1390,7 +1392,7 @@ describe('FieldNode', () => { } const addressSchema: SchemaOrSchemaFn
= (p) => { - disabled(p.street, () => true); + disabled(p.street, {when: () => true}); }; const data = signal<{name: string; address: Address}>({ @@ -1430,7 +1432,7 @@ describe('FieldNode', () => { it('should resolve predefined schema paths within the local context', () => { const s = schema<{a: string; b: string}>((p) => { - disabled(p.b, ({valueOf}) => valueOf(p.a) === 'disable-b'); + disabled(p.b, {when: ({valueOf}) => valueOf(p.a) === 'disable-b'}); }); const f = form( @@ -1448,7 +1450,7 @@ describe('FieldNode', () => { it('should resolve predefined schema paths deeply nested within the schema', () => { const s = schema<{a: string; b: string}>((p) => { - disabled(p.b, ({valueOf}) => valueOf(p.a) === 'disable-b'); + disabled(p.b, {when: ({valueOf}) => valueOf(p.a) === 'disable-b'}); }); const f = form( @@ -1472,9 +1474,11 @@ describe('FieldNode', () => { const f = form( signal(''), (p) => { - disabled(p, ({fieldTreeOf}) => { - fieldTreeOf(otherP); - return true; + disabled(p, { + when: ({fieldTreeOf}) => { + fieldTreeOf(otherP); + return true; + }, }); }, {injector: TestBed.inject(Injector)}, diff --git a/packages/forms/signals/test/node/recursive_logic.spec.ts b/packages/forms/signals/test/node/recursive_logic.spec.ts index 715682a55d8..f2048fb8166 100644 --- a/packages/forms/signals/test/node/recursive_logic.spec.ts +++ b/packages/forms/signals/test/node/recursive_logic.spec.ts @@ -34,8 +34,10 @@ function isNonNull(t: T): t is NonNullable { describe('recursive schema logic', () => { it('should support recursive logic', () => { const s = schema((p) => { - disabled(p.level, ({valueOf}) => { - return valueOf(p.level) % 2 === 0; + disabled(p.level, { + when: ({valueOf}) => { + return valueOf(p.level) % 2 === 0; + }, }); applyWhenValue(p.next, isNonNull, s); }); @@ -58,11 +60,11 @@ describe('recursive schema logic', () => { it('should support co-recursive logic', () => { const s1: Schema = schema((p) => { - disabled(p.level, ({valueOf}) => valueOf(p.level) % 2 === 0); + disabled(p.level, {when: ({valueOf}) => valueOf(p.level) % 2 === 0}); applyWhenValue(p.next, isNonNull, s2); }); const s2: Schema = schema((p) => { - disabled(p.level, ({valueOf}) => valueOf(p.level) % 2 === 0); + disabled(p.level, {when: ({valueOf}) => valueOf(p.level) % 2 === 0}); applyWhenValue(p.next, isNonNull, s1); }); const f = form( diff --git a/packages/forms/signals/test/node/submit.spec.ts b/packages/forms/signals/test/node/submit.spec.ts index 52c38a8f284..df682830bb7 100644 --- a/packages/forms/signals/test/node/submit.spec.ts +++ b/packages/forms/signals/test/node/submit.spec.ts @@ -423,7 +423,7 @@ describe('submit', () => { data, (name) => { // Disable first name when last name is empty. - disabled(name.first, ({valueOf}) => valueOf(name.last) === ''); + disabled(name.first, {when: ({valueOf}) => valueOf(name.last) === ''}); }, {injector: TestBed.inject(Injector)}, ); @@ -448,7 +448,7 @@ describe('submit', () => { data, (name) => { // Hide first name when last name is empty. - hidden(name.first, ({valueOf}) => valueOf(name.last) === ''); + hidden(name.first, {when: ({valueOf}) => valueOf(name.last) === ''}); }, {injector: TestBed.inject(Injector)}, ); @@ -473,7 +473,7 @@ describe('submit', () => { data, (name) => { // Make first name readonly when last name is empty. - readonly(name.first, ({valueOf}) => valueOf(name.last) === ''); + readonly(name.first, {when: ({valueOf}) => valueOf(name.last) === ''}); }, {injector: TestBed.inject(Injector)}, ); diff --git a/packages/forms/signals/test/web/dynamic_binding.spec.ts b/packages/forms/signals/test/web/dynamic_binding.spec.ts index 7d1ce20de2e..7c8bb3ee185 100644 --- a/packages/forms/signals/test/web/dynamic_binding.spec.ts +++ b/packages/forms/signals/test/web/dynamic_binding.spec.ts @@ -73,7 +73,7 @@ describe('createComponent', () => { const environmentInjector = TestBed.inject(EnvironmentInjector); const control = TestBed.runInInjectionContext(() => { return form(signal('initial value'), (p) => { - disabled(p, disabledSignal); + disabled(p, {when: disabledSignal}); }); }); diff --git a/packages/forms/signals/test/web/form_field.spec.ts b/packages/forms/signals/test/web/form_field.spec.ts index 90130936316..9e46a213c6a 100644 --- a/packages/forms/signals/test/web/form_field.spec.ts +++ b/packages/forms/signals/test/web/form_field.spec.ts @@ -324,7 +324,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); } @@ -359,7 +359,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(false), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly customControl = viewChild.required(CustomControlDir); } @@ -400,7 +400,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(false), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly customControl = viewChild.required(BaseControlDir); } @@ -431,7 +431,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(false), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly customControl = viewChild.required(CustomControl); } @@ -456,7 +456,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(false), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly customControl = viewChild.required(CustomControl); } @@ -482,7 +482,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(false), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly customControl = viewChild.required(CustomControl); } @@ -509,7 +509,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly dir = viewChild.required(TestDir); } @@ -541,7 +541,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(false), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly dir = viewChild.required(TestDir); } @@ -620,7 +620,7 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly customControl = viewChild.required(CustomControl); } @@ -663,7 +663,7 @@ describe('field directive', () => { class TestCmp { readonly data = signal(''); readonly f = form(this.data, (p) => { - disabled(p, () => 'Currently unavailable'); + disabled(p, {when: () => 'Currently unavailable'}); }); readonly customControl = viewChild.required(CustomControlDir); } @@ -694,7 +694,7 @@ describe('field directive', () => { class TestCmp { readonly data = signal(''); readonly f = form(this.data, (p) => { - disabled(p, () => 'Currently unavailable'); + disabled(p, {when: () => 'Currently unavailable'}); }); readonly customControl = viewChild.required(CustomControl); } @@ -724,7 +724,7 @@ describe('field directive', () => { class TestCmp { readonly data = signal(''); readonly f = form(this.data, (p) => { - disabled(p, () => 'Currently unavailable'); + disabled(p, {when: () => 'Currently unavailable'}); }); readonly customControl = viewChild.required(CustomControl); } @@ -750,8 +750,10 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, () => { - return this.disabled() ? 'b' : false; + disabled(p, { + when: () => { + return this.disabled() ? 'b' : false; + }, }); }); readonly dir = viewChild.required(TestDir); @@ -790,8 +792,10 @@ describe('field directive', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, () => { - return this.disabled() ? 'b' : false; + disabled(p, { + when: () => { + return this.disabled() ? 'b' : false; + }, }); }); readonly dir = viewChild.required(TestDir); @@ -822,7 +826,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal({x: '', y: ''}), (p) => { - disabled(p.x, () => 'Currently unavailable'); + disabled(p.x, {when: () => 'Currently unavailable'}); }); readonly field = signal(this.f.x); readonly customControl = viewChild.required(CustomControl); @@ -1053,7 +1057,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal(''), (p) => { - hidden(p, () => !visible()); + hidden(p, {when: () => !visible()}); }); readonly field = signal(this.f); readonly customControl = viewChild.required(CustomControlDir); @@ -1086,7 +1090,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal(''), (p) => { - hidden(p, () => !visible()); + hidden(p, {when: () => !visible()}); }); readonly field = signal(this.f); readonly customControl = viewChild.required(CustomControl); @@ -1115,7 +1119,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal(''), (p) => { - hidden(p, () => !visible()); + hidden(p, {when: () => !visible()}); }); readonly field = signal(this.f); readonly customControl = viewChild.required(CustomControl); @@ -1142,7 +1146,7 @@ describe('field directive', () => { class TestCmp { readonly hidden = signal(false); readonly f = form(signal(''), (p) => { - hidden(p, this.hidden); + hidden(p, {when: this.hidden}); }); readonly dir = viewChild.required(TestDir); } @@ -1174,7 +1178,7 @@ describe('field directive', () => { class TestCmp { readonly hidden = signal(false); readonly f = form(signal(''), (p) => { - hidden(p, this.hidden); + hidden(p, {when: this.hidden}); }); readonly dir = viewChild.required(TestDir); } @@ -1201,7 +1205,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal({x: 'a', y: 'b'}), (p) => { - hidden(p.x, () => true); + hidden(p.x, {when: () => true}); }); readonly field = signal(this.f.x); readonly customControl = viewChild.required(CustomControl); @@ -1223,7 +1227,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal(''), (p) => { - hidden(p, () => true); + hidden(p, {when: () => true}); }); } @@ -1246,7 +1250,7 @@ describe('field directive', () => { }) class TestCmp { readonly f = form(signal(''), (p) => { - hidden(p, isHidden); + hidden(p, {when: isHidden}); }); } @@ -1866,7 +1870,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(true); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); } @@ -1901,7 +1905,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); readonly child = viewChild.required(CustomControlDir); } @@ -1932,7 +1936,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); readonly customControl = viewChild.required(CustomControl); } @@ -1959,7 +1963,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); readonly child = viewChild.required(CustomControl); } @@ -1985,7 +1989,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); } @@ -2011,7 +2015,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); readonly dir = viewChild.required(TestDir); } @@ -2043,7 +2047,7 @@ describe('field directive', () => { class TestCmp { readonly readonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.readonly); + readonly(p, {when: this.readonly}); }); readonly dir = viewChild.required(TestDir); } @@ -4690,7 +4694,7 @@ describe('field directive', () => { `, }) class TestCmp { - f = form(signal(''), (p) => hidden(p, ({value}) => value() === '')); + f = form(signal(''), (p) => hidden(p, {when: ({value}) => value() === ''})); select = viewChild>('select'); options = ['one', 'two', 'three']; } @@ -4721,7 +4725,7 @@ describe('field directive', () => { `, }) class TestCmp { - f = form(signal(''), (p) => hidden(p, ({value}) => value() === '')); + f = form(signal(''), (p) => hidden(p, {when: ({value}) => value() === ''})); select = viewChild>('select'); options = ['one', 'two', 'three']; } @@ -4934,7 +4938,7 @@ describe('field directive', () => { myInput = viewChild.required(CustomInput); data = signal(''); f = form(this.data, (p) => { - disabled(p, () => 'Currently unavailable'); + disabled(p, {when: () => 'Currently unavailable'}); }); } @@ -4991,7 +4995,7 @@ describe('field directive', () => { myInput = viewChild.required(CustomInput); data = signal(''); f = form(this.data, (p) => { - hidden(p, ({value}) => value() === ''); + hidden(p, {when: ({value}) => value() === ''}); }); } @@ -5164,7 +5168,7 @@ describe('field directive', () => { model = signal(''); f = form(this.model, (p) => { required(p, {message: 'schema error'}); - disabled(p, ({value}) => (value() === 'disabled' ? 'schema disabled' : false)); + disabled(p, {when: ({value}) => (value() === 'disabled' ? 'schema disabled' : false)}); }); disabledReasons = [{message: 'manual disabled'}]; errors = [{kind: 'error', message: 'manual error'}]; diff --git a/packages/forms/signals/test/web/interop.spec.ts b/packages/forms/signals/test/web/interop.spec.ts index fa4d04fbfdb..45328911030 100644 --- a/packages/forms/signals/test/web/interop.spec.ts +++ b/packages/forms/signals/test/web/interop.spec.ts @@ -388,7 +388,7 @@ describe('ControlValueAccessor', () => { }) class TestCmp { f = form(signal('test'), (p) => { - disabled(p, () => !enabled()); + disabled(p, {when: () => !enabled()}); }); } @@ -516,7 +516,7 @@ describe('ControlValueAccessor', () => { class App { disabled = signal(false); readonly f = form(signal('test'), (f) => { - disabled(f, () => this.disabled()); + disabled(f, {when: () => this.disabled()}); }); } @@ -622,7 +622,7 @@ describe('ControlValueAccessor', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); readonly dir = viewChild.required(TestDir); } @@ -644,7 +644,7 @@ describe('ControlValueAccessor', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, this.disabled); + disabled(p, {when: this.disabled}); }); } @@ -725,8 +725,10 @@ describe('ControlValueAccessor', () => { class TestCmp { readonly disabled = signal(false); readonly f = form(signal(''), (p) => { - disabled(p, () => { - return this.disabled() ? 'Test reason' : false; + disabled(p, { + when: () => { + return this.disabled() ? 'Test reason' : false; + }, }); }); readonly dir = viewChild.required(TestDir); @@ -787,7 +789,7 @@ describe('ControlValueAccessor', () => { class TestCmp { readonly hidden = signal(false); readonly f = form(signal(''), (p) => { - hidden(p, this.hidden); + hidden(p, {when: this.hidden}); }); readonly dir = viewChild.required(TestDir); } @@ -923,7 +925,7 @@ describe('ControlValueAccessor', () => { class TestCmp { readonly isReadonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.isReadonly); + readonly(p, {when: this.isReadonly}); }); readonly dir = viewChild.required(TestDir); } @@ -945,7 +947,7 @@ describe('ControlValueAccessor', () => { class TestCmp { readonly isReadonly = signal(false); readonly f = form(signal(''), (p) => { - readonly(p, this.isReadonly); + readonly(p, {when: this.isReadonly}); }); } diff --git a/packages/forms/signals/test/web/signal_form_control_web.spec.ts b/packages/forms/signals/test/web/signal_form_control_web.spec.ts index da4ca61b338..9d4c93b3b10 100644 --- a/packages/forms/signals/test/web/signal_form_control_web.spec.ts +++ b/packages/forms/signals/test/web/signal_form_control_web.spec.ts @@ -103,7 +103,7 @@ describe('SignalFormControl (web)', () => { readonly signalControl = new SignalFormControl( 10, (p) => { - disabled(p, ({value}) => value() > 15); + disabled(p, {when: ({value}) => value() > 15}); }, {injector: inject(Injector)}, );