requiredIf()
π§ API mapβ
| I want to⦠| Details |
|---|---|
| See every accepted call style | Signatures |
| Conditionally require a value | Usage and behavior |
| Customize the failure message | Message configuration |
| Understand signal tracking | Reactive behavior |
| Compare it with unconditional presence | required() |
π Signaturesβ
requiredIf(condition)
requiredIf(condition, message)
requiredIf(condition, options)
condition should return boolean; its declared return type is intentionally unchecked to support self-references. The options customize only the failure message,
which may itself be reactive.
It is a concise alternative to required({ when }) when the condition does not need the validator
context:
requiredIf(() => businessAccount())
required({ when: () => businessAccount() })
π Usage and behaviorβ
Use requiredIf() when whether a value is mandatory depends on reactive application or form state:
const businessAccount = signal(false);
const myForm = form({
companyName: field('', [requiredIf(() => businessAccount())]),
});
myForm.companyName.required(); // false
businessAccount.set(true);
myForm.companyName.required(); // true
myForm.companyName.getError('required'); // { kind: 'required', ... }
When the condition returns false, the validator passes and contributes
required() === false metadata. When it returns true, it behaves exactly like
required(): it rejects null, undefined, '', and NaN, while
both false and empty arrays, sets, maps, and objects remain present values.
For conditional acceptance, use requiredTrue({ when: () => condition() }).
The condition may also depend on a sibling node declared in the same form:
const myForm = form({
accountType: field<'personal' | 'business'>('personal'),
companyName: field('', [
requiredIf(() => myForm.accountType() === 'business'),
]),
});
π¬ Message configurationβ
Pass a string for a static message:
field('', [requiredIf(() => businessAccount(), 'Enter a company name.')]);
Use an options object for a reactive message:
field('', [
requiredIf(() => businessAccount(), { message: () => translations().companyNameRequired })
]);
Returning undefined from the message function continues through node, Angular provider,
process-wide, and built-in message fallbacks. See
Validator messages.
β‘ Reactive behaviorβ
Signals read by condition are tracked. Changing one invalidates validation and required metadata;
the condition is evaluated again when either state is next consumed. A reactive consumer of
errors(), valid(), invalid(), validationStatus(), or required() observes the change.
The message function is evaluated only for an active, failing rule. Disabled, readonly, and hidden nodes skip validation until they become interactive again, while the configured required metadata continues to reflect the condition.
π Related referenceβ
Class self-referencesβ
A condition can reference the form being declared, directly or through a computed signal declared later in the class. No explicit form type, computed type, or callback return annotation is needed. The form's fields and the computed signal retain their inferred types.
import { computed } from '@angular/core';
import { field, form, required, requiredIf } from '@ngblocks/form-nodes';
class ProfileModel {
form = form({
other: field<number>(23, [required]),
subType: field<string>(null, [requiredIf(() => this.isTypeVisible())]),
});
isTypeVisible = computed(() => (this.form.other() ?? 0) > 30);
}
const model = new ProfileModel();
model.isTypeVisible(); // false
if (model.form.subType.required() || !model.form.valid()) throw new Error('The hidden subtype must be optional.');
model.form.other.set(31);
model.isTypeVisible(); // true
if (!model.form.subType.required() || !model.form.invalid()) throw new Error('The visible subtype must be required.');
model.form.other.set(23);
if (model.form.subType.required() || !model.form.valid()) throw new Error('The condition must remain reactive.');
The condition parameter is typed () => any to break TypeScript's circular contextual-return
inference, just like parameterless validator callbacks. This means TypeScript does not reject
non-boolean returns: always return a boolean. Parameterless when callbacks on other validators support the same convention; callbacks
receiving a context retain their checked boolean result. It does not
cause eager condition evaluation or change reactive tracking. A nullable numeric field still
requires null handling in comparisons, as shown by ?? 0 above.
For custom helper signatures and explicit return-type alternatives, see troubleshooting circular type inference.